blob: 1817799bf5fe4cee57607f2984537c628a5dabff [file] [log] [blame]
margaretha139d0f72017-11-14 18:56:22 +01001package de.ids_mannheim.korap.authentication;
Michael Hanl87106d12015-09-14 18:13:51 +02002
3import de.ids_mannheim.korap.exceptions.KustvaktException;
4import de.ids_mannheim.korap.exceptions.StatusCodes;
5import de.ids_mannheim.korap.user.DemoUser;
6import de.ids_mannheim.korap.user.TokenContext;
7import de.ids_mannheim.korap.utils.ConcurrentMultiMap;
Michael Hanl87106d12015-09-14 18:13:51 +02008import de.ids_mannheim.korap.utils.TimeUtils;
9import org.joda.time.DateTime;
10import org.slf4j.Logger;
Michael Hanlac113e52016-01-19 15:49:20 +010011import org.slf4j.LoggerFactory;
Michael Hanl87106d12015-09-14 18:13:51 +020012import org.springframework.cache.annotation.CacheEvict;
13import org.springframework.cache.annotation.Cacheable;
14
15import java.util.HashSet;
Michael Hanl2c3b0b12016-07-01 18:30:12 +020016import java.util.List;
Michael Hanl87106d12015-09-14 18:13:51 +020017import java.util.Map.Entry;
18import java.util.Set;
19import java.util.concurrent.ConcurrentHashMap;
20import java.util.concurrent.ConcurrentMap;
21
22/**
Michael Hanl8abaf9e2016-05-23 16:46:35 +020023 * session object to hold current user sessions and track inactive
24 * time to close
25 * unused sessions. Inactive sessions are not enforced until user
26 * makes a
Michael Hanl87106d12015-09-14 18:13:51 +020027 * request through thrift
Michael Hanl8abaf9e2016-05-23 16:46:35 +020028 *
Michael Hanl87106d12015-09-14 18:13:51 +020029 * @author hanl
30 */
Michael Hanl7368aa42016-02-05 18:15:47 +010031//todo: use simple ehcache!
Michael Hanl87106d12015-09-14 18:13:51 +020032public class SessionFactory implements Runnable {
33
Michael Hanlac113e52016-01-19 15:49:20 +010034 private static Logger jlog = LoggerFactory.getLogger(SessionFactory.class);
Michael Hanl87106d12015-09-14 18:13:51 +020035
margarethaf18298b2017-09-14 22:14:32 +020036 public static ConcurrentMap<String, TokenContext> sessionsObject;
37 public static ConcurrentMap<String, DateTime> timeCheck;
38 public static ConcurrentMultiMap<String, String> loggedInRecord;
Michael Hanl87106d12015-09-14 18:13:51 +020039 // private final ConcurrentMultiMap<String, Long> failedLogins;
40 private final boolean multipleEnabled;
41 private final int inactive;
42
Michael Hanl8abaf9e2016-05-23 16:46:35 +020043
44 public SessionFactory (boolean multipleEnabled, int inactive) {
Michael Hanl87106d12015-09-14 18:13:51 +020045 jlog.debug("allow multiple sessions per user: '{}'", multipleEnabled);
46 this.multipleEnabled = multipleEnabled;
47 this.inactive = inactive;
48 this.sessionsObject = new ConcurrentHashMap<>();
49 this.timeCheck = new ConcurrentHashMap<>();
50 this.loggedInRecord = new ConcurrentMultiMap<>();
51 }
52
Michael Hanl8abaf9e2016-05-23 16:46:35 +020053
54 public boolean hasSession (TokenContext context) {
Michael Hanl87106d12015-09-14 18:13:51 +020055 if (context.getUsername().equalsIgnoreCase(DemoUser.DEMOUSER_NAME))
56 return false;
Michael Hanl2c3b0b12016-07-01 18:30:12 +020057
58 List<String> value = loggedInRecord.get(context.getUsername());
59 return value != null && !value.isEmpty();
Michael Hanl87106d12015-09-14 18:13:51 +020060 }
61
Michael Hanl2c3b0b12016-07-01 18:30:12 +020062 // todo: remove this!
Michael Hanl87106d12015-09-14 18:13:51 +020063 @Cacheable("session")
Michael Hanl8abaf9e2016-05-23 16:46:35 +020064 public TokenContext getSession (String token) throws KustvaktException {
Michael Hanl87106d12015-09-14 18:13:51 +020065 jlog.debug("logged in users: {}", loggedInRecord);
66 TokenContext context = sessionsObject.get(token);
67 if (context != null) {
Michael Hanl99cb9632016-06-29 16:24:40 +020068 // fixme: set context to respecitve expiratin interval and return context. handler checks expiration later!
Michael Hanl87106d12015-09-14 18:13:51 +020069 if (isUserSessionValid(token)) {
70 resetInterval(token);
Michael Hanl8abaf9e2016-05-23 16:46:35 +020071 }
72 else
Michael Hanl87106d12015-09-14 18:13:51 +020073 throw new KustvaktException(StatusCodes.EXPIRED);
74
Michael Hanl8abaf9e2016-05-23 16:46:35 +020075 }
Michael Hanl99cb9632016-06-29 16:24:40 +020076 return context;
Michael Hanl87106d12015-09-14 18:13:51 +020077 }
78
Michael Hanl8abaf9e2016-05-23 16:46:35 +020079
Michael Hanl87106d12015-09-14 18:13:51 +020080 //todo: ?!
81 @CacheEvict(value = "session", key = "#session.token")
Michael Hanl8abaf9e2016-05-23 16:46:35 +020082 public void putSession (final String token, final TokenContext activeUser)
Michael Hanl87106d12015-09-14 18:13:51 +020083 throws KustvaktException {
84 if (!hasSession(activeUser) | multipleEnabled) {
85 loggedInRecord.put(activeUser.getUsername(), token);
86 sessionsObject.put(token, activeUser);
87 timeCheck.put(token, TimeUtils.getNow());
Michael Hanl8abaf9e2016-05-23 16:46:35 +020088 }
89 else {
Michael Hanl87106d12015-09-14 18:13:51 +020090 removeAll(activeUser);
91 throw new KustvaktException(StatusCodes.ALREADY_LOGGED_IN);
92 }
93 }
94
Michael Hanl8abaf9e2016-05-23 16:46:35 +020095
96 public void removeAll (final TokenContext activeUser) {
Michael Hanl87106d12015-09-14 18:13:51 +020097 for (String existing : loggedInRecord.get(activeUser.getUsername())) {
98 timeCheck.remove(existing);
99 sessionsObject.remove(existing);
100 }
101 loggedInRecord.remove(activeUser.getUsername());
102 }
103
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200104
Michael Hanl87106d12015-09-14 18:13:51 +0200105 @CacheEvict(value = "session", key = "#session.token")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200106 public void removeSession (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200107 String username = sessionsObject.get(token).getUsername();
108 loggedInRecord.remove(username, token);
109 if (loggedInRecord.get(username).isEmpty())
110 loggedInRecord.remove(username);
111 timeCheck.remove(token);
112 sessionsObject.remove(token);
113 }
114
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200115
Michael Hanl87106d12015-09-14 18:13:51 +0200116 /**
117 * reset inactive time interval to 0
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200118 *
Michael Hanl87106d12015-09-14 18:13:51 +0200119 * @param token
120 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200121 private void resetInterval (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200122 timeCheck.put(token, TimeUtils.getNow());
123 }
124
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200125
Michael Hanl87106d12015-09-14 18:13:51 +0200126 /**
127 * if user possesses a valid non-expired session token
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200128 *
Michael Hanl87106d12015-09-14 18:13:51 +0200129 * @param token
130 * @return validity of user to request a backend function
131 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200132 private boolean isUserSessionValid (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200133 if (timeCheck.containsKey(token)) {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200134 if (TimeUtils.plusSeconds(timeCheck.get(token).getMillis(),
135 inactive).isAfterNow()) {
Michael Hanl87106d12015-09-14 18:13:51 +0200136 jlog.debug("user has session");
137 return true;
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200138 }
139 else
Michael Hanl87106d12015-09-14 18:13:51 +0200140 jlog.debug("user with token {} has an invalid session", token);
141 }
142 return false;
143 }
144
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200145
Michael Hanl87106d12015-09-14 18:13:51 +0200146 /**
147 * clean inactive sessions from session object
148 * TODO: persist userdata to database when session times out!
149 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200150 private void timeoutMaintenance () {
Michael Hanl3520dcd2016-02-08 19:11:37 +0100151 jlog.trace("running session cleanup thread");
Michael Hanl87106d12015-09-14 18:13:51 +0200152 Set<String> inactive = new HashSet<>();
153 for (Entry<String, DateTime> entry : timeCheck.entrySet()) {
154 if (!isUserSessionValid(entry.getKey())) {
155 TokenContext user = sessionsObject.get(entry.getKey());
Michael Hanl3520dcd2016-02-08 19:11:37 +0100156 jlog.trace("removing user session for user {}",
Michael Hanl87106d12015-09-14 18:13:51 +0200157 user.getUsername());
158 inactive.add(user.getUsername());
159 removeSession(entry.getKey());
160 }
161 }
Michael Hanl7368aa42016-02-05 18:15:47 +0100162 // fixme: not doing anything!
Michael Hanl87106d12015-09-14 18:13:51 +0200163 if (inactive.size() > 0)
Michael Hanl3520dcd2016-02-08 19:11:37 +0100164 jlog.trace("removing inactive user session for users '{}' ",
Michael Hanl87106d12015-09-14 18:13:51 +0200165 inactive);
Michael Hanl87106d12015-09-14 18:13:51 +0200166 }
167
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200168
Michael Hanl87106d12015-09-14 18:13:51 +0200169 /**
170 * run cleanup-thread
171 */
172 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200173 public void run () {
Michael Hanl87106d12015-09-14 18:13:51 +0200174 timeoutMaintenance();
Michael Hanl7368aa42016-02-05 18:15:47 +0100175 if (loggedInRecord.size() > 0)
176 jlog.debug("logged users: {}", loggedInRecord.toString());
Michael Hanl87106d12015-09-14 18:13:51 +0200177 }
178}