blob: 26e316a3609f339dce23e56fd63666f890ec4013 [file] [log] [blame]
Michael Hanl87106d12015-09-14 18:13:51 +02001package de.ids_mannheim.korap.security.auth;
2
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;
8import de.ids_mannheim.korap.utils.KustvaktLogger;
9import de.ids_mannheim.korap.utils.TimeUtils;
10import org.joda.time.DateTime;
11import org.slf4j.Logger;
12import org.springframework.cache.annotation.CacheEvict;
13import org.springframework.cache.annotation.Cacheable;
14
15import java.util.HashSet;
16import java.util.Map.Entry;
17import java.util.Set;
18import java.util.concurrent.ConcurrentHashMap;
19import java.util.concurrent.ConcurrentMap;
20
21/**
22 * session object to hold current user sessions and track inactive time to close
23 * unused sessions. Inactive sessions are not enforced until user makes a
24 * request through thrift
25 *
26 * @author hanl
27 */
28public class SessionFactory implements Runnable {
29
30 private static Logger jlog = KustvaktLogger.initiate(SessionFactory.class);
31
32 private final ConcurrentMap<String, TokenContext> sessionsObject;
33 private final ConcurrentMap<String, DateTime> timeCheck;
34 private final ConcurrentMultiMap<String, String> loggedInRecord;
35 // private final ConcurrentMultiMap<String, Long> failedLogins;
36 private final boolean multipleEnabled;
37 private final int inactive;
38
39 public SessionFactory(boolean multipleEnabled, int inactive) {
40 jlog.debug("allow multiple sessions per user: '{}'", multipleEnabled);
41 this.multipleEnabled = multipleEnabled;
42 this.inactive = inactive;
43 this.sessionsObject = new ConcurrentHashMap<>();
44 this.timeCheck = new ConcurrentHashMap<>();
45 this.loggedInRecord = new ConcurrentMultiMap<>();
46 }
47
48 public boolean hasSession(TokenContext context) {
49 if (context.getUsername().equalsIgnoreCase(DemoUser.DEMOUSER_NAME))
50 return false;
51 if (loggedInRecord.containsKey(context.getUsername()) && !loggedInRecord
52 .get(context.getUsername()).isEmpty())
53 return true;
54 return false;
55 }
56
57 @Cacheable("session")
58 public TokenContext getSession(String token) throws KustvaktException {
59 jlog.debug("logged in users: {}", loggedInRecord);
60 TokenContext context = sessionsObject.get(token);
61 if (context != null) {
62 if (isUserSessionValid(token)) {
63 resetInterval(token);
64 return context;
65 }else
66 throw new KustvaktException(StatusCodes.EXPIRED);
67
68 }else
69 throw new KustvaktException(StatusCodes.PERMISSION_DENIED);
70 }
71
72 //todo: ?!
73 @CacheEvict(value = "session", key = "#session.token")
74 public void putSession(final String token, final TokenContext activeUser)
75 throws KustvaktException {
76 if (!hasSession(activeUser) | multipleEnabled) {
77 loggedInRecord.put(activeUser.getUsername(), token);
78 sessionsObject.put(token, activeUser);
79 timeCheck.put(token, TimeUtils.getNow());
80 }else {
81 removeAll(activeUser);
82 throw new KustvaktException(StatusCodes.ALREADY_LOGGED_IN);
83 }
84 }
85
86 public void removeAll(final TokenContext activeUser) {
87 for (String existing : loggedInRecord.get(activeUser.getUsername())) {
88 timeCheck.remove(existing);
89 sessionsObject.remove(existing);
90 }
91 loggedInRecord.remove(activeUser.getUsername());
92 }
93
94 @CacheEvict(value = "session", key = "#session.token")
95 public void removeSession(String token) {
96 String username = sessionsObject.get(token).getUsername();
97 loggedInRecord.remove(username, token);
98 if (loggedInRecord.get(username).isEmpty())
99 loggedInRecord.remove(username);
100 timeCheck.remove(token);
101 sessionsObject.remove(token);
102 }
103
104 /**
105 * reset inactive time interval to 0
106 *
107 * @param token
108 */
109 private void resetInterval(String token) {
110 timeCheck.put(token, TimeUtils.getNow());
111 }
112
113 /**
114 * if user possesses a valid non-expired session token
115 *
116 * @param token
117 * @return validity of user to request a backend function
118 */
119 private boolean isUserSessionValid(String token) {
120 if (timeCheck.containsKey(token)) {
121 if (TimeUtils.plusSeconds(timeCheck.get(token).getMillis(),
122 inactive).isAfterNow()) {
123 jlog.debug("user has session");
124 return true;
125 }else
126 jlog.debug("user with token {} has an invalid session", token);
127 }
128 return false;
129 }
130
131 /**
132 * clean inactive sessions from session object
133 * TODO: persist userdata to database when session times out!
134 */
135 private void timeoutMaintenance() {
136 jlog.debug("running session cleanup thread");
137 Set<String> inactive = new HashSet<>();
138 for (Entry<String, DateTime> entry : timeCheck.entrySet()) {
139 if (!isUserSessionValid(entry.getKey())) {
140 TokenContext user = sessionsObject.get(entry.getKey());
141 jlog.debug("removing user session for user {}",
142 user.getUsername());
143 inactive.add(user.getUsername());
144 removeSession(entry.getKey());
145 }
146 }
147 if (inactive.size() > 0)
148 jlog.debug("removing inactive user session for users '{}' ",
149 inactive);
150
151 // keys:
152 // for (String key : failedLogins.getKeySet()) {
153 // DateTime d = new DateTime(failedLogins.get(key).get(1));
154 // if (d.isBeforeNow()) {
155 // failedLogins.remove(key);
156 // jlog.info("removed failed login counts due to expiration for user {}", key);
157 // continue keys;
158 // }
159 // }
160 }
161
162 /**
163 * run cleanup-thread
164 */
165 @Override
166 public void run() {
167 timeoutMaintenance();
168 jlog.debug("logged users: {}", loggedInRecord.toString());
169
170 }
171}