blob: 6d425c29d8bb9844fc7da4d3ed65e808c9c6bf46 [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;
margaretha0e8f4e72018-04-05 14:11:52 +02005import de.ids_mannheim.korap.security.context.TokenContext;
Michael Hanl87106d12015-09-14 18:13:51 +02006import de.ids_mannheim.korap.user.DemoUser;
Michael Hanl87106d12015-09-14 18:13:51 +02007import de.ids_mannheim.korap.utils.ConcurrentMultiMap;
Michael Hanl87106d12015-09-14 18:13:51 +02008import de.ids_mannheim.korap.utils.TimeUtils;
margaretha49cb6882018-07-04 04:19:54 +02009
10import org.apache.logging.log4j.LogManager;
11import org.apache.logging.log4j.Logger;
Michael Hanl87106d12015-09-14 18:13:51 +020012import org.joda.time.DateTime;
Michael Hanl87106d12015-09-14 18:13:51 +020013import org.springframework.cache.annotation.CacheEvict;
14import org.springframework.cache.annotation.Cacheable;
15
16import java.util.HashSet;
Michael Hanl2c3b0b12016-07-01 18:30:12 +020017import java.util.List;
Michael Hanl87106d12015-09-14 18:13:51 +020018import java.util.Map.Entry;
19import java.util.Set;
20import java.util.concurrent.ConcurrentHashMap;
21import java.util.concurrent.ConcurrentMap;
22
23/**
Michael Hanl8abaf9e2016-05-23 16:46:35 +020024 * session object to hold current user sessions and track inactive
25 * time to close
26 * unused sessions. Inactive sessions are not enforced until user
27 * makes a
Michael Hanl87106d12015-09-14 18:13:51 +020028 * request through thrift
Michael Hanl8abaf9e2016-05-23 16:46:35 +020029 *
Michael Hanl87106d12015-09-14 18:13:51 +020030 * @author hanl
31 */
Michael Hanl7368aa42016-02-05 18:15:47 +010032//todo: use simple ehcache!
Michael Hanl87106d12015-09-14 18:13:51 +020033public class SessionFactory implements Runnable {
34
margaretha49cb6882018-07-04 04:19:54 +020035 private static Logger jlog = LogManager.getLogger(SessionFactory.class);
Michael Hanl87106d12015-09-14 18:13:51 +020036
margarethaf18298b2017-09-14 22:14:32 +020037 public static ConcurrentMap<String, TokenContext> sessionsObject;
38 public static ConcurrentMap<String, DateTime> timeCheck;
39 public static ConcurrentMultiMap<String, String> loggedInRecord;
Michael Hanl87106d12015-09-14 18:13:51 +020040 // private final ConcurrentMultiMap<String, Long> failedLogins;
41 private final boolean multipleEnabled;
42 private final int inactive;
43
Michael Hanl8abaf9e2016-05-23 16:46:35 +020044
45 public SessionFactory (boolean multipleEnabled, int inactive) {
margaretha49cb6882018-07-04 04:19:54 +020046 jlog.debug("allow multiple sessions per user: "+ multipleEnabled);
Michael Hanl87106d12015-09-14 18:13:51 +020047 this.multipleEnabled = multipleEnabled;
48 this.inactive = inactive;
49 this.sessionsObject = new ConcurrentHashMap<>();
50 this.timeCheck = new ConcurrentHashMap<>();
51 this.loggedInRecord = new ConcurrentMultiMap<>();
52 }
53
Michael Hanl8abaf9e2016-05-23 16:46:35 +020054
55 public boolean hasSession (TokenContext context) {
Michael Hanl87106d12015-09-14 18:13:51 +020056 if (context.getUsername().equalsIgnoreCase(DemoUser.DEMOUSER_NAME))
57 return false;
Michael Hanl2c3b0b12016-07-01 18:30:12 +020058
59 List<String> value = loggedInRecord.get(context.getUsername());
60 return value != null && !value.isEmpty();
Michael Hanl87106d12015-09-14 18:13:51 +020061 }
62
Michael Hanl2c3b0b12016-07-01 18:30:12 +020063 // todo: remove this!
Michael Hanl87106d12015-09-14 18:13:51 +020064 @Cacheable("session")
Michael Hanl8abaf9e2016-05-23 16:46:35 +020065 public TokenContext getSession (String token) throws KustvaktException {
margaretha49cb6882018-07-04 04:19:54 +020066 jlog.debug("logged in users: "+ loggedInRecord);
Michael Hanl87106d12015-09-14 18:13:51 +020067 TokenContext context = sessionsObject.get(token);
68 if (context != null) {
Michael Hanl99cb9632016-06-29 16:24:40 +020069 // fixme: set context to respecitve expiratin interval and return context. handler checks expiration later!
Michael Hanl87106d12015-09-14 18:13:51 +020070 if (isUserSessionValid(token)) {
71 resetInterval(token);
Michael Hanl8abaf9e2016-05-23 16:46:35 +020072 }
73 else
Michael Hanl87106d12015-09-14 18:13:51 +020074 throw new KustvaktException(StatusCodes.EXPIRED);
75
Michael Hanl8abaf9e2016-05-23 16:46:35 +020076 }
Michael Hanl99cb9632016-06-29 16:24:40 +020077 return context;
Michael Hanl87106d12015-09-14 18:13:51 +020078 }
79
Michael Hanl8abaf9e2016-05-23 16:46:35 +020080
Michael Hanl87106d12015-09-14 18:13:51 +020081 //todo: ?!
82 @CacheEvict(value = "session", key = "#session.token")
Michael Hanl8abaf9e2016-05-23 16:46:35 +020083 public void putSession (final String token, final TokenContext activeUser)
Michael Hanl87106d12015-09-14 18:13:51 +020084 throws KustvaktException {
85 if (!hasSession(activeUser) | multipleEnabled) {
86 loggedInRecord.put(activeUser.getUsername(), token);
87 sessionsObject.put(token, activeUser);
88 timeCheck.put(token, TimeUtils.getNow());
Michael Hanl8abaf9e2016-05-23 16:46:35 +020089 }
90 else {
Michael Hanl87106d12015-09-14 18:13:51 +020091 removeAll(activeUser);
92 throw new KustvaktException(StatusCodes.ALREADY_LOGGED_IN);
93 }
94 }
95
Michael Hanl8abaf9e2016-05-23 16:46:35 +020096
97 public void removeAll (final TokenContext activeUser) {
Michael Hanl87106d12015-09-14 18:13:51 +020098 for (String existing : loggedInRecord.get(activeUser.getUsername())) {
99 timeCheck.remove(existing);
100 sessionsObject.remove(existing);
101 }
102 loggedInRecord.remove(activeUser.getUsername());
103 }
104
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200105
Michael Hanl87106d12015-09-14 18:13:51 +0200106 @CacheEvict(value = "session", key = "#session.token")
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200107 public void removeSession (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200108 String username = sessionsObject.get(token).getUsername();
109 loggedInRecord.remove(username, token);
110 if (loggedInRecord.get(username).isEmpty())
111 loggedInRecord.remove(username);
112 timeCheck.remove(token);
113 sessionsObject.remove(token);
114 }
115
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200116
Michael Hanl87106d12015-09-14 18:13:51 +0200117 /**
118 * reset inactive time interval to 0
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200119 *
Michael Hanl87106d12015-09-14 18:13:51 +0200120 * @param token
121 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200122 private void resetInterval (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200123 timeCheck.put(token, TimeUtils.getNow());
124 }
125
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200126
Michael Hanl87106d12015-09-14 18:13:51 +0200127 /**
128 * if user possesses a valid non-expired session token
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200129 *
Michael Hanl87106d12015-09-14 18:13:51 +0200130 * @param token
131 * @return validity of user to request a backend function
132 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200133 private boolean isUserSessionValid (String token) {
Michael Hanl87106d12015-09-14 18:13:51 +0200134 if (timeCheck.containsKey(token)) {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200135 if (TimeUtils.plusSeconds(timeCheck.get(token).getMillis(),
136 inactive).isAfterNow()) {
Michael Hanl87106d12015-09-14 18:13:51 +0200137 jlog.debug("user has session");
138 return true;
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200139 }
140 else
margaretha49cb6882018-07-04 04:19:54 +0200141 jlog.debug("user with token "+token+" has an invalid session");
Michael Hanl87106d12015-09-14 18:13:51 +0200142 }
143 return false;
144 }
145
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200146
Michael Hanl87106d12015-09-14 18:13:51 +0200147 /**
148 * clean inactive sessions from session object
149 * TODO: persist userdata to database when session times out!
150 */
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200151 private void timeoutMaintenance () {
Michael Hanl3520dcd2016-02-08 19:11:37 +0100152 jlog.trace("running session cleanup thread");
Michael Hanl87106d12015-09-14 18:13:51 +0200153 Set<String> inactive = new HashSet<>();
154 for (Entry<String, DateTime> entry : timeCheck.entrySet()) {
155 if (!isUserSessionValid(entry.getKey())) {
156 TokenContext user = sessionsObject.get(entry.getKey());
margaretha49cb6882018-07-04 04:19:54 +0200157 jlog.trace("removing user session for user "+
Michael Hanl87106d12015-09-14 18:13:51 +0200158 user.getUsername());
159 inactive.add(user.getUsername());
160 removeSession(entry.getKey());
161 }
162 }
Michael Hanl7368aa42016-02-05 18:15:47 +0100163 // fixme: not doing anything!
Michael Hanl87106d12015-09-14 18:13:51 +0200164 if (inactive.size() > 0)
margaretha49cb6882018-07-04 04:19:54 +0200165 jlog.trace("removing inactive user session for users "+
Michael Hanl87106d12015-09-14 18:13:51 +0200166 inactive);
Michael Hanl87106d12015-09-14 18:13:51 +0200167 }
168
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200169
Michael Hanl87106d12015-09-14 18:13:51 +0200170 /**
171 * run cleanup-thread
172 */
173 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200174 public void run () {
Michael Hanl87106d12015-09-14 18:13:51 +0200175 timeoutMaintenance();
Michael Hanl7368aa42016-02-05 18:15:47 +0100176 if (loggedInRecord.size() > 0)
margaretha49cb6882018-07-04 04:19:54 +0200177 jlog.debug("logged users: "+ loggedInRecord.toString());
Michael Hanl87106d12015-09-14 18:13:51 +0200178 }
179}