blob: 8a8ac3d5c9afbfdda108ccb133dd9ce652fcca85 [file] [log] [blame]
margaretha139d0f72017-11-14 18:56:22 +01001package de.ids_mannheim.korap.authentication;
Michael Hanl87106d12015-09-14 18:13:51 +02002
margarethaebc54962017-05-29 13:23:07 +02003import java.io.IOException;
4import java.io.UnsupportedEncodingException;
5import java.net.InetAddress;
6import java.net.UnknownHostException;
7import java.security.NoSuchAlgorithmException;
margaretha4b5c1412017-11-15 20:55:04 +01008import java.util.Arrays;
margarethaebc54962017-05-29 13:23:07 +02009import java.util.Collection;
10import java.util.Map;
11
12import javax.ws.rs.core.HttpHeaders;
13import javax.ws.rs.core.MultivaluedMap;
14
15import org.slf4j.Logger;
16import org.slf4j.LoggerFactory;
17
18// import com.novell.ldap.*; search() funktioniert nicht korrekt, ausgewechselt gegen unboundID's Bibliothek 20.04.17/FB
19//Using JAR from unboundID:
20import com.unboundid.ldap.sdk.LDAPException;
Bodmoca3dcfb2017-05-24 16:36:00 +020021
Michael Hanl87106d12015-09-14 18:13:51 +020022import de.ids_mannheim.korap.auditing.AuditRecord;
Michael Hanl00b64e02016-05-24 20:24:27 +020023import de.ids_mannheim.korap.config.Attributes;
margaretha2afb97d2017-12-07 19:18:44 +010024import de.ids_mannheim.korap.config.AuthenticationMethod;
Michael Hanldaf86602016-05-12 14:31:52 +020025import de.ids_mannheim.korap.config.BeansFactory;
margaretha56e8e552017-12-05 16:31:21 +010026import de.ids_mannheim.korap.config.FullConfiguration;
margaretha2afb97d2017-12-07 19:18:44 +010027import de.ids_mannheim.korap.config.TokenType;
Michael Hanl87106d12015-09-14 18:13:51 +020028import de.ids_mannheim.korap.config.URIParam;
margarethaebc54962017-05-29 13:23:07 +020029import de.ids_mannheim.korap.exceptions.EmptyResultException;
30import de.ids_mannheim.korap.exceptions.KustvaktException;
31import de.ids_mannheim.korap.exceptions.NotAuthorizedException;
32import de.ids_mannheim.korap.exceptions.StatusCodes;
33import de.ids_mannheim.korap.exceptions.WrappedException;
Michael Hanl19390652016-01-16 11:01:24 +010034import de.ids_mannheim.korap.interfaces.AuthenticationIface;
35import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
36import de.ids_mannheim.korap.interfaces.EncryptionIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020037import de.ids_mannheim.korap.interfaces.ValidatorIface;
margaretha62055f72017-04-11 19:17:43 +020038import de.ids_mannheim.korap.interfaces.db.AdminHandlerIface;
Michael Hanlf21773f2015-10-16 23:02:31 +020039import de.ids_mannheim.korap.interfaces.db.AuditingIface;
40import de.ids_mannheim.korap.interfaces.db.EntityHandlerIface;
Michael Hanl415276b2016-01-29 16:39:37 +010041import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020042import de.ids_mannheim.korap.interfaces.defaults.ApacheValidator;
margarethaebc54962017-05-29 13:23:07 +020043import de.ids_mannheim.korap.user.DemoUser;
44import de.ids_mannheim.korap.user.KorAPUser;
45import de.ids_mannheim.korap.user.ShibUser;
46import de.ids_mannheim.korap.user.TokenContext;
47import de.ids_mannheim.korap.user.User;
Bodmoca3dcfb2017-05-24 16:36:00 +020048import de.ids_mannheim.korap.user.User.CorpusAccess;
margarethaebc54962017-05-29 13:23:07 +020049import de.ids_mannheim.korap.user.User.Location;
50import de.ids_mannheim.korap.user.UserDetails;
51import de.ids_mannheim.korap.user.UserSettings;
52import de.ids_mannheim.korap.user.Userdata;
Michael Hanl87106d12015-09-14 18:13:51 +020053import de.ids_mannheim.korap.utils.TimeUtils;
Bodmoca3dcfb2017-05-24 16:36:00 +020054
Michael Hanl87106d12015-09-14 18:13:51 +020055/**
margarethaebc54962017-05-29 13:23:07 +020056 * contains the logic to authentication and registration processes. Uses
57 * interface implementations (AuthenticationIface) for different databases and
58 * handlers
Michael Hanl8abaf9e2016-05-23 16:46:35 +020059 *
Michael Hanl87106d12015-09-14 18:13:51 +020060 * @author hanl
61 */
62public class KustvaktAuthenticationManager extends AuthenticationManagerIface {
63
margarethaebc54962017-05-29 13:23:07 +020064 private static Logger jlog = LoggerFactory.getLogger(KustvaktAuthenticationManager.class);
65 private EncryptionIface crypto;
66 private EntityHandlerIface entHandler;
67 private AdminHandlerIface adminHandler;
68 private AuditingIface auditing;
margaretha56e8e552017-12-05 16:31:21 +010069 private FullConfiguration config;
margarethaebc54962017-05-29 13:23:07 +020070 private Collection userdatadaos;
71 private LoginCounter counter;
72 private ValidatorIface validator;
margaretha2afb97d2017-12-07 19:18:44 +010073
margarethaebc54962017-05-29 13:23:07 +020074 public KustvaktAuthenticationManager(EntityHandlerIface userdb, AdminHandlerIface admindb, EncryptionIface crypto,
margaretha56e8e552017-12-05 16:31:21 +010075 FullConfiguration config, AuditingIface auditer, Collection<UserDataDbIface> userdatadaos) {
margarethaebc54962017-05-29 13:23:07 +020076 this.entHandler = userdb;
77 this.adminHandler = admindb;
78 this.config = config;
79 this.crypto = crypto;
80 this.auditing = auditer;
81 this.counter = new LoginCounter(config);
82 this.userdatadaos = userdatadaos;
83 // todo: load via beancontext
84 try {
85 this.validator = new ApacheValidator();
86 } catch (IOException e) {
87 e.printStackTrace();
88 }
89 }
Michael Hanl87106d12015-09-14 18:13:51 +020090
margarethaebc54962017-05-29 13:23:07 +020091 /**
92 * get session object if token was a session token
93 *
94 * @param token
95 * @param host
96 * @param useragent
97 * @return
98 * @throws KustvaktException
99 */
100 @Override
margarethadfecb4b2017-12-12 19:32:30 +0100101 public TokenContext getTokenContext(TokenType type, String token,
margaretha4b5c1412017-11-15 20:55:04 +0100102 String host, String useragent) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200103
margaretha4de41192017-11-15 11:47:11 +0100104 AuthenticationIface provider = getProvider(type , null);
Michael Hanl99cb9632016-06-29 16:24:40 +0200105
margarethaebc54962017-05-29 13:23:07 +0200106 if (provider == null)
107 // throw exception for missing type parameter
margaretha4b5c1412017-11-15 20:55:04 +0100108 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
109 "token type not defined or found", "token_type");
Michael Hanl19390652016-01-16 11:01:24 +0100110
margarethaebc54962017-05-29 13:23:07 +0200111 TokenContext context = provider.getTokenContext(token);
margarethaebc54962017-05-29 13:23:07 +0200112 // if (!matchStatus(host, useragent, context))
113 // provider.removeUserSession(token);
114 return context;
115 }
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200116
margarethaebc54962017-05-29 13:23:07 +0200117 @Override
118 public User getUser(String username) throws KustvaktException {
119 // User user;
120 // Object value = this.getCacheValue(username);
Michael Hanl87106d12015-09-14 18:13:51 +0200121
margarethaebc54962017-05-29 13:23:07 +0200122 if (User.UserFactory.isDemo(username))
123 return User.UserFactory.getDemoUser();
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200124
margarethaebc54962017-05-29 13:23:07 +0200125 // if (value != null) {
126 // Map map = (Map) value;
127 // user = User.UserFactory.toUser(map);
128 // }
129 // else {
130 // user = entHandler.getAccount(username);
131 // this.storeInCache(username, user.toCache());
132 // todo: not valid. for the duration of the session, the host should not
133 // change!
134 // }
135 // todo:
136 // user.addField(Attributes.HOST, context.getHostAddress());
137 // user.addField(Attributes.USER_AGENT, context.getUserAgent());
margaretha0b63de42017-12-20 18:48:09 +0100138
139 //EM:copied from EntityDao
140 KorAPUser user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
141 user.setUsername(username);
142 return user;
143// return entHandler.getAccount(username);
margarethaebc54962017-05-29 13:23:07 +0200144 }
margaretha7d0165c2018-02-26 15:31:37 +0100145
146 @Override
147 public User getUser (String username, String method)
148 throws KustvaktException {
149 KorAPUser user = new KorAPUser();
150 user.setUsername(username);
151 String email = null;
152 switch (method.toLowerCase()) {
153 case "ldap":
154 email = config.getTestEmail();
155 break;
156 default:
157 email = config.getTestEmail();
158 break;
159 }
160 user.setEmail(email);
161 return user;
162 }
Michael Hanl87106d12015-09-14 18:13:51 +0200163
margarethaebc54962017-05-29 13:23:07 +0200164 public TokenContext refresh(TokenContext context) throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100165 AuthenticationIface provider = getProvider(context.getTokenType(), null);
margarethaebc54962017-05-29 13:23:07 +0200166 if (provider == null) {
167 // todo:
168 }
Michael Hanlc4446022016-02-12 18:03:17 +0100169
margarethaebc54962017-05-29 13:23:07 +0200170 try {
171 provider.removeUserSession(context.getToken());
172 User user = getUser(context.getUsername());
173 return provider.createTokenContext(user, context.params());
174 } catch (KustvaktException e) {
175 throw new WrappedException(e, StatusCodes.LOGIN_FAILED);
176 }
177 }
Michael Hanl87106d12015-09-14 18:13:51 +0200178
margarethafde771a2017-11-14 15:02:10 +0100179 /** EM: fix type is not flexible
margarethaebc54962017-05-29 13:23:07 +0200180 * @param type
181 * @param attributes
182 * contains username and password to authenticate the user.
183 * Depending of the authentication schema, may contain other
184 * values as well
185 * @return User
186 * @throws KustvaktException
187 */
188 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100189 public User authenticate(AuthenticationMethod method, String username, String password, Map<String, Object> attributes)
margarethaebc54962017-05-29 13:23:07 +0200190 throws KustvaktException {
191 User user;
margaretha2afb97d2017-12-07 19:18:44 +0100192 switch (method) {
margaretha139d0f72017-11-14 18:56:22 +0100193 case SHIBBOLETH:
margarethaebc54962017-05-29 13:23:07 +0200194 // todo:
195 user = authenticateShib(attributes);
196 break;
margaretha139d0f72017-11-14 18:56:22 +0100197 case LDAP:
margarethaebc54962017-05-29 13:23:07 +0200198 // IdM/LDAP: (09.02.17/FB)
199 user = authenticateIdM(username, password, attributes);
200 break;
201 default:
202 user = authenticate(username, password, attributes);
203 break;
204 }
205 auditing.audit(AuditRecord.serviceRecord(user.getId(), StatusCodes.LOGIN_SUCCESSFUL, user.toString()));
206 return user;
207 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200208
margarethaebc54962017-05-29 13:23:07 +0200209 // a. set location depending on X-Forwarded-For.
210 // X-Forwarded-For: clientIP, ProxyID, ProxyID...
211 // the following private address spaces may be used to define intranet
212 // spaces:
213 // 10.0.0.0 - 10.255.255.255 (10/8 prefix)
214 // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
215 // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
216 // b. set corpusAccess depending on location:
Bodmo11fb6f82017-06-01 11:39:15 +0200217 // c. DemoUser only gets corpusAccess=FREE.
margarethaebc54962017-05-29 13:23:07 +0200218 // 16.05.17/FB
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200219
margarethaebc54962017-05-29 13:23:07 +0200220 @Override
221 public void setAccessAndLocation(User user, HttpHeaders headers) {
margarethaebc54962017-05-29 13:23:07 +0200222 MultivaluedMap<String, String> headerMap = headers.getRequestHeaders();
223 Location location = Location.EXTERN;
224 CorpusAccess corpusAccess = CorpusAccess.FREE;
margarethaa89c3f92017-05-30 19:02:08 +0200225
Bodmo11fb6f82017-06-01 11:39:15 +0200226 if( user instanceof DemoUser )
227 {
228 // to be absolutely sure:
229 user.setCorpusAccess(User.CorpusAccess.FREE);
margarethaa86b1412018-02-21 20:40:35 +0100230 jlog.debug("setAccessAndLocation: DemoUser: location=%s, access=%s.\n", user.locationtoString(), user.accesstoString());
Bodmo11fb6f82017-06-01 11:39:15 +0200231 return;
232 }
margarethaa89c3f92017-05-30 19:02:08 +0200233
margaretha58e18632018-02-15 13:04:42 +0100234 if (headerMap != null && headerMap.containsKey(com.google.common.net.HttpHeaders.X_FORWARDED_FOR)) {
Michael Hanl87106d12015-09-14 18:13:51 +0200235
margaretha58e18632018-02-15 13:04:42 +0100236 String[] vals = headerMap.getFirst(com.google.common.net.HttpHeaders.X_FORWARDED_FOR).split(",");
margarethaebc54962017-05-29 13:23:07 +0200237 String clientAddress = vals[0];
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200238
margarethaebc54962017-05-29 13:23:07 +0200239 try {
240 InetAddress ip = InetAddress.getByName(clientAddress);
241 if (ip.isSiteLocalAddress()){
margarethaebc54962017-05-29 13:23:07 +0200242 location = Location.INTERN;
margarethaebc54962017-05-29 13:23:07 +0200243 corpusAccess = CorpusAccess.ALL;
244 }
margarethaa89c3f92017-05-30 19:02:08 +0200245 else{
246 corpusAccess = CorpusAccess.PUB;
247 }
248
margarethabb486302017-11-21 13:47:22 +0100249 jlog.debug(String.format("X-Forwarded-For : '%s' (%d values) -> %s\n",
250 Arrays.toString(vals), vals.length, vals[0]));
251 jlog.debug(String.format("X-Forwarded-For : location = %s corpusAccess = %s\n",
252 location == Location.INTERN ? "INTERN" : "EXTERN", corpusAccess == CorpusAccess.ALL ? "ALL"
253 : corpusAccess == CorpusAccess.PUB ? "PUB" : "FREE"));
Michael Hanl87106d12015-09-14 18:13:51 +0200254
margarethaebc54962017-05-29 13:23:07 +0200255 } catch (UnknownHostException e) {
256 // TODO Auto-generated catch block
257 e.printStackTrace();
258 }
Michael Hanl87106d12015-09-14 18:13:51 +0200259
margarethaebc54962017-05-29 13:23:07 +0200260 user.setLocation(location);
261 user.setCorpusAccess(corpusAccess);
margarethaa86b1412018-02-21 20:40:35 +0100262
263 jlog.debug("setAccessAndLocation: KorAPUser: location=%s, access=%s.\n", user.locationtoString(), user.accesstoString());
Bodmoc125bf12017-06-01 16:23:59 +0200264
margarethaebc54962017-05-29 13:23:07 +0200265 }
266 } // getAccess
Michael Hanl87106d12015-09-14 18:13:51 +0200267
margarethaebc54962017-05-29 13:23:07 +0200268 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100269 public TokenContext createTokenContext(User user, Map<String, Object> attr, TokenType type)
margarethaebc54962017-05-29 13:23:07 +0200270 throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100271 // use api token
272 AuthenticationIface provider = getProvider(type, TokenType.API);
Michael Hanl87106d12015-09-14 18:13:51 +0200273
margaretha38d530e2017-07-11 19:06:50 +0200274 // EM: not in the new DB
275// if (attr.get(Attributes.SCOPES) != null)
276// this.getUserData(user, UserDetails.class);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200277
margarethaebc54962017-05-29 13:23:07 +0200278 TokenContext context = provider.createTokenContext(user, attr);
279 if (context == null)
280 throw new KustvaktException(StatusCodes.NOT_SUPPORTED);
281 context.setUserAgent((String) attr.get(Attributes.USER_AGENT));
282 context.setHostAddress(Attributes.HOST);
283 return context;
284 }
Michael Hanl87106d12015-09-14 18:13:51 +0200285
margarethaebc54962017-05-29 13:23:07 +0200286 // todo: test
287 @Deprecated
288 private boolean matchStatus(String host, String useragent, TokenContext context) {
289 if (host.equals(context.getHostAddress())) {
290 if (useragent.equals(context.getUserAgent()))
291 return true;
292 }
293 return false;
294 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200295
margarethaebc54962017-05-29 13:23:07 +0200296 private User authenticateShib(Map<String, Object> attributes) throws KustvaktException {
297 // todo use persistent id, since eppn is not unique
298 String eppn = (String) attributes.get(Attributes.EPPN);
Michael Hanl87106d12015-09-14 18:13:51 +0200299
margarethaebc54962017-05-29 13:23:07 +0200300 if (eppn == null || eppn.isEmpty())
301 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200302
margarethaebc54962017-05-29 13:23:07 +0200303 if (!attributes.containsKey(Attributes.EMAIL) && validator.isValid(eppn, Attributes.EMAIL))
304 attributes.put(Attributes.EMAIL, eppn);
Michael Hanl87106d12015-09-14 18:13:51 +0200305
margarethaebc54962017-05-29 13:23:07 +0200306 User user = null;
307 if (isRegistered(eppn))
308 user = createShibbUserAccount(attributes);
309 return user;
310 }
Michael Hanl87106d12015-09-14 18:13:51 +0200311
margarethaebc54962017-05-29 13:23:07 +0200312 // todo: what if attributes null?
313 private User authenticate(String username, String password, Map<String, Object> attr) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200314
margarethaebc54962017-05-29 13:23:07 +0200315 Map<String, Object> attributes = validator.validateMap(attr);
316 User unknown;
317 // just to make sure that the plain password does not appear anywhere in
318 // the logs!
Michael Hanl87106d12015-09-14 18:13:51 +0200319
margarethaebc54962017-05-29 13:23:07 +0200320 try {
321 validator.validateEntry(username, Attributes.USERNAME);
322 } catch (KustvaktException e) {
323 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, username);
324 }
Michael Hanl87106d12015-09-14 18:13:51 +0200325
margarethaebc54962017-05-29 13:23:07 +0200326 if (username == null || username.isEmpty())
327 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
328 StatusCodes.LOGIN_FAILED);
329 else {
330 try {
331 unknown = entHandler.getAccount(username);
332 } catch (EmptyResultException e) {
333 // mask exception to disable user guessing in possible attacks
334 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
335 StatusCodes.LOGIN_FAILED, username);
336 } catch (KustvaktException e) {
337 jlog.error("Error: {}", e);
338 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, attributes.toString());
339 }
340 }
Michael Hanl87106d12015-09-14 18:13:51 +0200341
margarethaebc54962017-05-29 13:23:07 +0200342 boolean isAdmin = adminHandler.isAdmin(unknown.getId());
margaretha45667922018-01-25 21:23:03 +0100343 unknown.setSystemAdmin(isAdmin);
margarethaf18298b2017-09-14 22:14:32 +0200344 jlog.debug("Authentication: found username " + unknown.getUsername());
Michael Hanl87106d12015-09-14 18:13:51 +0200345
margarethaebc54962017-05-29 13:23:07 +0200346 if (unknown instanceof KorAPUser) {
347 if (password == null || password.isEmpty())
348 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.BAD_CREDENTIALS),
349 StatusCodes.LOGIN_FAILED, username);
Michael Hanl87106d12015-09-14 18:13:51 +0200350
margarethaebc54962017-05-29 13:23:07 +0200351 KorAPUser user = (KorAPUser) unknown;
352 boolean check = crypto.checkHash(password, user.getPassword());
Michael Hanl87106d12015-09-14 18:13:51 +0200353
margarethaebc54962017-05-29 13:23:07 +0200354 if (!check) {
355 // the fail counter only applies for wrong password
356 jlog.warn("Wrong Password!");
357 processLoginFail(unknown);
358 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
359 StatusCodes.LOGIN_FAILED, username);
360 }
Michael Hanl87106d12015-09-14 18:13:51 +0200361
margarethaebc54962017-05-29 13:23:07 +0200362 // bad credentials error has precedence over account locked or
363 // unconfirmed codes
364 // since latter can lead to account guessing of third parties
365 if (user.isAccountLocked()) {
366 URIParam param = (URIParam) user.getField(URIParam.class);
Michael Hanl87106d12015-09-14 18:13:51 +0200367
margarethaebc54962017-05-29 13:23:07 +0200368 if (param.hasValues()) {
369 jlog.debug("Account is not yet activated for user '{}'", user.getUsername());
370 if (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
371 jlog.error("URI token is expired. Deleting account for user {}", user.getUsername());
372 deleteAccount(user);
373 throw new WrappedException(
374 new KustvaktException(unknown.getId(), StatusCodes.EXPIRED,
375 "account confirmation uri has expired!", param.getUriFragment()),
376 StatusCodes.LOGIN_FAILED, username);
377 }
378 throw new WrappedException(
379 new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
380 StatusCodes.LOGIN_FAILED, username);
381 }
382 jlog.error("ACCESS DENIED: account not active for '{}'", unknown.getUsername());
383 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
384 StatusCodes.LOGIN_FAILED, username);
385 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200386
margarethaebc54962017-05-29 13:23:07 +0200387 } else if (unknown instanceof ShibUser) {
388 // todo
389 }
margarethaf18298b2017-09-14 22:14:32 +0200390 jlog.debug("Authentication done: "+unknown);
margarethaebc54962017-05-29 13:23:07 +0200391 return unknown;
392 }
393
394 /**
395 * authenticate using IdM (Identitätsmanagement) accessed by LDAP.
396 *
397 * @param username
398 * @param password
399 * @param attr
400 * @return
401 * @throws KustvaktException
402 * @date 09.02.17/FB
403 */
404 // todo: what if attributes null?
Bodmo3d6bd352017-04-25 11:31:39 +0200405
margarethaebc54962017-05-29 13:23:07 +0200406 private User authenticateIdM(String username, String password, Map<String, Object> attr) throws KustvaktException {
Bodmo3d6bd352017-04-25 11:31:39 +0200407
margarethaebc54962017-05-29 13:23:07 +0200408 Map<String, Object> attributes = validator.validateMap(attr);
409 User unknown = null;
410 // just to make sure that the plain password does not appear anywhere in
411 // the logs!
Bodmo3d6bd352017-04-25 11:31:39 +0200412
margarethaebc54962017-05-29 13:23:07 +0200413 System.out.printf("Debug: authenticateIdM: entering for '%s'...\n", username);
Bodmo3d6bd352017-04-25 11:31:39 +0200414
margarethaebc54962017-05-29 13:23:07 +0200415 /**
416 * wozu Apache Validatoren für User/Passwort für IdM/LDAP? siehe
417 * validation.properties. Abgeschaltet 21.04.17/FB try {
418 * validator.validateEntry(username, Attributes.USERNAME); } catch
419 * (KustvaktException e) { throw new WrappedException(e,
420 * StatusCodes.LOGIN_FAILED, username); }
421 */
422 if (username == null || username.isEmpty() || password == null || password.isEmpty())
423 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
424 StatusCodes.LOGIN_FAILED);
Bodmo3d6bd352017-04-25 11:31:39 +0200425
margarethaebc54962017-05-29 13:23:07 +0200426 // LDAP Access:
427 try {
428 // todo: unknown = ...
margaretha65b67142017-05-29 16:23:16 +0200429 int ret = LdapAuth3.login(username, password, config.getLdapConfig());
margarethaebc54962017-05-29 13:23:07 +0200430 System.out.printf("Debug: autenticationIdM: Ldap.login(%s) returns: %d.\n", username, ret);
431 if (ret != LdapAuth3.LDAP_AUTH_ROK) {
432 jlog.error("LdapAuth3.login(username='{}') returns '{}'='{}'!", username, ret,
433 LdapAuth3.getErrMessage(ret));
Bodmo3d6bd352017-04-25 11:31:39 +0200434
margarethaebc54962017-05-29 13:23:07 +0200435 // mask exception to disable user guessing in possible attacks
436 /*
437 * by Hanl throw new WrappedException(new
438 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
439 * StatusCodes.LOGIN_FAILED, username);
440 */
441 throw new WrappedException(new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + ret,
442 LdapAuth3.getErrMessage(ret), null), StatusCodes.LOGIN_FAILED, username);
443 }
444 } catch (LDAPException e) {
Bodmo3d6bd352017-04-25 11:31:39 +0200445
margarethaebc54962017-05-29 13:23:07 +0200446 jlog.error("Error: username='{}' -> '{}'!", username, e);
447 // mask exception to disable user guessing in possible attacks
448 /*
449 * by Hanl: throw new WrappedException(new
450 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
451 * StatusCodes.LOGIN_FAILED, username);
452 */
453 throw new WrappedException(
454 new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + LdapAuth3.LDAP_AUTH_RINTERR,
455 LdapAuth3.getErrMessage(LdapAuth3.LDAP_AUTH_RINTERR), null),
456 StatusCodes.LOGIN_FAILED, username);
457 }
Bodmo3d6bd352017-04-25 11:31:39 +0200458
margarethaebc54962017-05-29 13:23:07 +0200459 // Create a User
460 // TODO: KorAPUser für solche mit einem bestehenden Account
461 // DefaultUser sonst.
462 User user = new KorAPUser();
463 user.setUsername(username);
464 /*
465 * folgender Code funktioniert hier noch nicht, da die Headers noch
466 * nicht ausgewertet worden sind - 23.05.17/FB Object o =
467 * attr.get(Attributes.LOCATION); String loc = (String)o.toString(); int
468 * location = Integer.parseInt(loc); user.setLocation(location);
469 * user.setCorpusAccess(Integer.parseInt(attr.get(Attributes.
470 * CORPUS_ACCESS).toString()));
471 */
472 unknown = user;
Michael Hanl87106d12015-09-14 18:13:51 +0200473
margarethaebc54962017-05-29 13:23:07 +0200474 jlog.trace("Authentication: found username " + unknown.getUsername());
Michael Hanl87106d12015-09-14 18:13:51 +0200475
margarethaebc54962017-05-29 13:23:07 +0200476 if (unknown instanceof KorAPUser) {
477 /*
478 * password already checked using LDAP: if (password == null ||
479 * password.isEmpty()) throw new WrappedException(new
480 * KustvaktException( unknown.getId(), StatusCodes.BAD_CREDENTIALS),
481 * StatusCodes.LOGIN_FAILED, username);
482 *
483 * KorAPUser user = (KorAPUser) unknown; boolean check =
484 * crypto.checkHash(password, user.getPassword());
485 *
486 * if (!check) { // the fail counter only applies for wrong password
487 * jlog.warn("Wrong Password!"); processLoginFail(unknown); throw
488 * new WrappedException(new KustvaktException(user.getId(),
489 * StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
490 * username); }
491 */
492 // bad credentials error has precedence over account locked or
493 // unconfirmed codes
494 // since latter can lead to account guessing of third parties
495 /*
496 * if (user.isAccountLocked()) {
497 *
498 * URIParam param = (URIParam) user.getField(URIParam.class);
499 *
500 * if (param.hasValues()) {
501 * jlog.debug("Account is not yet activated for user '{}'",
502 * user.getUsername()); if
503 * (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
504 * jlog.error( "URI token is expired. Deleting account for user {}",
505 * user.getUsername()); deleteAccount(user); throw new
506 * WrappedException(new KustvaktException( unknown.getId(),
507 * StatusCodes.EXPIRED, "account confirmation uri has expired!",
508 * param.getUriFragment()), StatusCodes.LOGIN_FAILED, username); }
509 * throw new WrappedException(new KustvaktException(
510 * unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
511 * StatusCodes.LOGIN_FAILED, username); }
512 * jlog.error("ACCESS DENIED: account not active for '{}'",
513 * unknown.getUsername()); throw new WrappedException(new
514 * KustvaktException( unknown.getId(),
515 * StatusCodes.ACCOUNT_DEACTIVATED), StatusCodes.LOGIN_FAILED,
516 * username); }
517 */
Michael Hanl87106d12015-09-14 18:13:51 +0200518
margarethaebc54962017-05-29 13:23:07 +0200519 } else if (unknown instanceof ShibUser) {
520 // todo
521 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200522
margarethaebc54962017-05-29 13:23:07 +0200523 jlog.debug("Authentication done: " + username);
524 return unknown;
Michael Hanl19390652016-01-16 11:01:24 +0100525
margarethaebc54962017-05-29 13:23:07 +0200526 } // authenticateIdM
Michael Hanl87106d12015-09-14 18:13:51 +0200527
margarethaebc54962017-05-29 13:23:07 +0200528 public boolean isRegistered(String username) {
529 User user;
530 if (username == null || username.isEmpty())
531 return false;
532 // throw new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT,
533 // "username must be set", username);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200534
margarethaebc54962017-05-29 13:23:07 +0200535 try {
536 user = entHandler.getAccount(username);
537 } catch (EmptyResultException e) {
538 jlog.debug("user does not exist ({})", username);
539 return false;
Michael Hanl87106d12015-09-14 18:13:51 +0200540
margarethaebc54962017-05-29 13:23:07 +0200541 } catch (KustvaktException e) {
542 jlog.error("KorAPException", e.string());
543 return false;
544 // throw new KustvaktException(username,
545 // StatusCodes.ILLEGAL_ARGUMENT,
546 // "username invalid", username);
547 }
548 return user != null;
549 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200550
margarethaebc54962017-05-29 13:23:07 +0200551 public void logout(TokenContext context) throws KustvaktException {
552 try {
margaretha2afb97d2017-12-07 19:18:44 +0100553 AuthenticationIface provider = getProvider(context.getTokenType(), null);
Michael Hanl87106d12015-09-14 18:13:51 +0200554
margarethaebc54962017-05-29 13:23:07 +0200555 if (provider == null) {
margaretha4de41192017-11-15 11:47:11 +0100556 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "Authentication "
margaretha2afb97d2017-12-07 19:18:44 +0100557 + "provider not supported!", context.getTokenType().displayName());
margarethaebc54962017-05-29 13:23:07 +0200558 }
559 provider.removeUserSession(context.getToken());
560 } catch (KustvaktException e) {
561 throw new WrappedException(e, StatusCodes.LOGOUT_FAILED, context.toString());
562 }
563 auditing.audit(
564 AuditRecord.serviceRecord(context.getUsername(), StatusCodes.LOGOUT_SUCCESSFUL, context.toString()));
565 this.removeCacheEntry(context.getToken());
566 }
Michael Hanl87106d12015-09-14 18:13:51 +0200567
margarethaebc54962017-05-29 13:23:07 +0200568 private void processLoginFail(User user) throws KustvaktException {
569 counter.registerFail(user.getUsername());
570 if (!counter.validate(user.getUsername())) {
571 try {
572 this.lockAccount(user);
573 } catch (KustvaktException e) {
574 jlog.error("user account could not be locked", e);
575 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
576 }
577 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
578 StatusCodes.LOGIN_FAILED);
579 }
580 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200581
margarethaebc54962017-05-29 13:23:07 +0200582 public void lockAccount(User user) throws KustvaktException {
583 if (!(user instanceof KorAPUser))
584 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200585
margarethaebc54962017-05-29 13:23:07 +0200586 KorAPUser u = (KorAPUser) user;
587 u.setAccountLocked(true);
588 jlog.info("locking account for user: {}", user.getUsername());
589 entHandler.updateAccount(u);
590 }
Michael Hanl87106d12015-09-14 18:13:51 +0200591
margarethaebc54962017-05-29 13:23:07 +0200592 public KorAPUser checkPasswordAllowance(KorAPUser user, String oldPassword, String newPassword)
593 throws KustvaktException {
594 String dbPassword = user.getPassword();
Michael Hanl87106d12015-09-14 18:13:51 +0200595
margarethaebc54962017-05-29 13:23:07 +0200596 if (oldPassword.trim().equals(newPassword.trim())) {
597 // TODO: special error StatusCodes for this?
598 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT),
599 StatusCodes.PASSWORD_RESET_FAILED, newPassword);
600 }
Michael Hanl87106d12015-09-14 18:13:51 +0200601
margarethaebc54962017-05-29 13:23:07 +0200602 boolean check = crypto.checkHash(oldPassword, dbPassword);
Michael Hanl87106d12015-09-14 18:13:51 +0200603
margarethaebc54962017-05-29 13:23:07 +0200604 if (!check)
605 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
606 StatusCodes.PASSWORD_RESET_FAILED);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200607
margarethaebc54962017-05-29 13:23:07 +0200608 try {
609 user.setPassword(crypto.secureHash(newPassword));
610 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
611 throw new WrappedException(
612 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassword),
613 StatusCodes.PASSWORD_RESET_FAILED, user.toString(), newPassword);
614 }
615 return user;
616 }
Michael Hanl87106d12015-09-14 18:13:51 +0200617
margarethaebc54962017-05-29 13:23:07 +0200618 // fixme: use clientinfo for logging/auditing?! = from where did he access
619 // the reset function?
620 @Override
621 public void resetPassword(String uriFragment, String username, String newPassphrase) throws KustvaktException {
622 try {
623 validator.validateEntry(username, Attributes.USERNAME);
624 validator.validateEntry(newPassphrase, Attributes.PASSWORD);
625 } catch (KustvaktException e) {
626 jlog.error("Error: {}", e.string());
627 throw new WrappedException(
628 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
629 StatusCodes.PASSWORD_RESET_FAILED, username, newPassphrase);
630 }
Michael Hanl87106d12015-09-14 18:13:51 +0200631
margarethaebc54962017-05-29 13:23:07 +0200632 try {
633 newPassphrase = crypto.secureHash(newPassphrase);
634 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
635 jlog.error("Encoding/Algorithm Error", e);
636 throw new WrappedException(
637 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
638 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment, newPassphrase);
639 }
640 int result = entHandler.resetPassphrase(username, uriFragment, newPassphrase);
Michael Hanl87106d12015-09-14 18:13:51 +0200641
margarethaebc54962017-05-29 13:23:07 +0200642 if (result == 0)
643 throw new WrappedException(
644 new KustvaktException(username, StatusCodes.EXPIRED, "URI fragment expired", uriFragment),
645 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment);
646 else if (result == 1)
647 jlog.info("successfully reset password for user {}", username);
648 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200649
margarethaebc54962017-05-29 13:23:07 +0200650 public void confirmRegistration(String uriFragment, String username) throws KustvaktException {
651 try {
652 validator.validateEntry(username, Attributes.USERNAME);
653 } catch (KustvaktException e) {
654 jlog.error("Error: {}", e.string());
655 throw new WrappedException(e, StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
656 }
657 int r = entHandler.activateAccount(username, uriFragment);
658 if (r == 0) {
659 User user;
660 try {
661 user = entHandler.getAccount(username);
662 } catch (EmptyResultException e) {
663 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
664 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
665 }
666 entHandler.deleteAccount(user.getId());
667 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.EXPIRED),
668 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
669 } else if (r == 1)
670 jlog.info("successfully confirmed user registration for user {}", username);
671 // register successful audit!
672 }
Michael Hanl87106d12015-09-14 18:13:51 +0200673
margarethaebc54962017-05-29 13:23:07 +0200674 /**
675 * @param attributes
676 * @return
677 * @throws KustvaktException
678 */
679 // fixme: remove clientinfo object (not needed), use json representation to
680 // get stuff
681 public User createUserAccount(Map<String, Object> attributes, boolean confirmation_required)
682 throws KustvaktException {
683 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200684
margarethaebc54962017-05-29 13:23:07 +0200685 if (safeMap.get(Attributes.USERNAME) == null || ((String) safeMap.get(Attributes.USERNAME)).isEmpty())
686 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "username must be set", "username");
687 if (safeMap.get(Attributes.PASSWORD) == null || ((String) safeMap.get(Attributes.PASSWORD)).isEmpty())
688 throw new KustvaktException(safeMap.get(Attributes.USERNAME), StatusCodes.ILLEGAL_ARGUMENT,
689 "password must be set", "password");
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200690
margarethaebc54962017-05-29 13:23:07 +0200691 String username = validator.validateEntry((String) safeMap.get(Attributes.USERNAME), Attributes.USERNAME);
692 String password = validator.validateEntry((String) safeMap.get(Attributes.PASSWORD), Attributes.PASSWORD);
693 String hash;
694 try {
695 hash = crypto.secureHash(password);
696 } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
697 jlog.error("Encryption error", e);
698 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT);
699 }
Michael Hanl87106d12015-09-14 18:13:51 +0200700
margarethaebc54962017-05-29 13:23:07 +0200701 KorAPUser user = User.UserFactory.getUser(username);
702 Object id = attributes.get(Attributes.ID);
703 if (id != null && id instanceof Integer)
704 user.setId((Integer) id);
Michael Hanl87106d12015-09-14 18:13:51 +0200705
margarethaebc54962017-05-29 13:23:07 +0200706 user.setAccountLocked(confirmation_required);
707 if (confirmation_required) {
708 URIParam param = new URIParam(crypto.createToken(),
709 TimeUtils.plusSeconds(config.getTokenTTL()).getMillis());
710 user.addField(param);
711 }
712 user.setPassword(hash);
Michael Hanl7368aa42016-02-05 18:15:47 +0100713
margarethaebc54962017-05-29 13:23:07 +0200714 String o = (String) attributes.get(Attributes.IS_ADMIN);
margaretha62055f72017-04-11 19:17:43 +0200715 boolean b = Boolean.parseBoolean(o);
margaretha45667922018-01-25 21:23:03 +0100716 user.setSystemAdmin(b);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200717
margarethaebc54962017-05-29 13:23:07 +0200718 try {
719 UserDetails details = new UserDetails();
720 details.read(safeMap, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200721
margarethaebc54962017-05-29 13:23:07 +0200722 UserSettings settings = new UserSettings();
723 settings.read(safeMap, true);
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100724
margarethaebc54962017-05-29 13:23:07 +0200725 jlog.info("Creating new user account for user {}", user.getUsername());
726 entHandler.createAccount(user);
margaretha45667922018-01-25 21:23:03 +0100727 if (user.isSystemAdmin() && user instanceof KorAPUser) {
margarethaebc54962017-05-29 13:23:07 +0200728 adminHandler.addAccount(user);
729 user.setCorpusAccess(CorpusAccess.ALL);
730 }
731 details.setUserId(user.getId());
732 settings.setUserId(user.getId());
Michael Hanl87106d12015-09-14 18:13:51 +0200733
margarethaebc54962017-05-29 13:23:07 +0200734 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
735 // todo: remove this
736 assert dao != null;
737 dao.store(details);
738 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
739 assert dao != null;
740 dao.store(settings);
741 } catch (KustvaktException e) {
742 jlog.error("Error: {}", e.string());
743 throw new WrappedException(e, StatusCodes.CREATE_ACCOUNT_FAILED, user.toString());
744 }
Michael Hanl87106d12015-09-14 18:13:51 +0200745
margarethaebc54962017-05-29 13:23:07 +0200746 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.CREATE_ACCOUNT_SUCCESSFUL));
747 return user;
748 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200749
margarethaebc54962017-05-29 13:23:07 +0200750 // todo:
751 private ShibUser createShibbUserAccount(Map<String, Object> attributes) throws KustvaktException {
752 jlog.debug("creating shibboleth user account for user attr: {}", attributes);
753 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl87106d12015-09-14 18:13:51 +0200754
margarethaebc54962017-05-29 13:23:07 +0200755 // todo eppn non-unique.join with idp or use persistent_id as username
756 // identifier
757 ShibUser user = User.UserFactory.getShibInstance((String) safeMap.get(Attributes.EPPN),
758 (String) safeMap.get(Attributes.MAIL), (String) safeMap.get(Attributes.CN));
759 user.setAffiliation((String) safeMap.get(Attributes.EDU_AFFIL));
760 user.setAccountCreation(TimeUtils.getNow().getMillis());
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200761
margarethaebc54962017-05-29 13:23:07 +0200762 UserDetails d = new UserDetails();
763 d.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200764
margarethaebc54962017-05-29 13:23:07 +0200765 UserSettings s = new UserSettings();
766 s.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200767
margarethaebc54962017-05-29 13:23:07 +0200768 entHandler.createAccount(user);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200769
margarethaebc54962017-05-29 13:23:07 +0200770 s.setUserId(user.getId());
771 d.setUserId(user.getId());
Michael Hanl25aac542016-02-01 18:16:44 +0100772
margarethaebc54962017-05-29 13:23:07 +0200773 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
774 assert dao != null;
775 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100776
margarethaebc54962017-05-29 13:23:07 +0200777 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
778 assert dao != null;
779 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100780
margarethaebc54962017-05-29 13:23:07 +0200781 return user;
782 }
Michael Hanl25aac542016-02-01 18:16:44 +0100783
margarethaebc54962017-05-29 13:23:07 +0200784 /**
785 * link shibboleth and korap user account to one another.
786 *
787 * @param current
788 * currently logged in user
789 * @param for_name
790 * foreign user name the current account should be linked to
791 * @param transstrat
792 * transfer status of user data (details, settings, user queries)
793 * 0 = the currently logged in data should be kept 1 = the
794 * foreign account data should be kept
795 * @throws NotAuthorizedException
796 * @throws KustvaktException
797 */
798 // todo:
799 public void accountLink(User current, String for_name, int transstrat) throws KustvaktException {
800 // User foreign = entHandler.getAccount(for_name);
Michael Hanl87106d12015-09-14 18:13:51 +0200801
margarethaebc54962017-05-29 13:23:07 +0200802 // if (current.getAccountLink() == null && current.getAccountLink()
803 // .isEmpty()) {
804 // if (current instanceof KorAPUser && foreign instanceof ShibUser) {
805 // if (transstrat == 1)
806 // current.transfer(foreign);
807 //// foreign.setAccountLink(current.getUsername());
808 //// current.setAccountLink(foreign.getUsername());
809 // // entHandler.purgeDetails(foreign);
810 // // entHandler.purgeSettings(foreign);
811 // }else if (foreign instanceof KorAPUser
812 // && current instanceof ShibUser) {
813 // if (transstrat == 0)
814 // foreign.transfer(current);
815 //// current.setAccountLink(foreign.getUsername());
816 // // entHandler.purgeDetails(current);
817 // // entHandler.purgeSettings(current);
818 // // entHandler.purgeSettings(current);
819 // }
820 // entHandler.updateAccount(current);
821 // entHandler.updateAccount(foreign);
822 // }
823 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200824
margarethaebc54962017-05-29 13:23:07 +0200825 // todo: test and rest usage?!
826 public boolean updateAccount(User user) throws KustvaktException {
827 boolean result;
828 if (user instanceof DemoUser)
829 throw new KustvaktException(user.getId(), StatusCodes.REQUEST_INVALID,
830 "account not updateable for demo user", user.getUsername());
831 else {
832 // crypto.validate(user);
833 try {
834 result = entHandler.updateAccount(user) > 0;
835 } catch (KustvaktException e) {
836 jlog.error("Error: {}", e.string());
837 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
838 }
839 }
840 if (result) {
841 // this.removeCacheEntry(user.getUsername());
842 auditing.audit(
843 AuditRecord.serviceRecord(user.getId(), StatusCodes.UPDATE_ACCOUNT_SUCCESSFUL, user.toString()));
844 }
845 return result;
846 }
Michael Hanl87106d12015-09-14 18:13:51 +0200847
margarethaebc54962017-05-29 13:23:07 +0200848 public boolean deleteAccount(User user) throws KustvaktException {
849 boolean result;
850 if (user instanceof DemoUser)
851 return true;
852 else {
853 try {
854 result = entHandler.deleteAccount(user.getId()) > 0;
855 } catch (KustvaktException e) {
856 jlog.error("Error: {}", e.string());
857 throw new WrappedException(e, StatusCodes.DELETE_ACCOUNT_FAILED);
858 }
859 }
860 if (result) {
861 // this.removeCacheEntry(user.getUsername());
862 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.DELETE_ACCOUNT_SUCCESSFUL,
863 user.toString()));
864 }
865 return result;
866 }
Michael Hanl87106d12015-09-14 18:13:51 +0200867
margarethaebc54962017-05-29 13:23:07 +0200868 public Object[] validateResetPasswordRequest(String username, String email) throws KustvaktException {
869 String uritoken;
870 validator.validateEntry(email, Attributes.EMAIL);
871 User ident;
872 try {
873 ident = entHandler.getAccount(username);
874 if (ident instanceof DemoUser)
875 // throw new
876 // NotAuthorizedException(StatusCodes.PERMISSION_DENIED,
877 // "password reset now allowed for DemoUser", "");
878 throw new WrappedException(username, StatusCodes.PASSWORD_RESET_FAILED, username);
879 } catch (EmptyResultException e) {
880 throw new WrappedException(
881 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "username not found", username),
882 StatusCodes.PASSWORD_RESET_FAILED, username);
883 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200884
margarethaebc54962017-05-29 13:23:07 +0200885 Userdata data = this.getUserData(ident, UserDetails.class);
886 KorAPUser user = (KorAPUser) ident;
Michael Hanl87106d12015-09-14 18:13:51 +0200887
margarethaebc54962017-05-29 13:23:07 +0200888 if (!email.equals(data.get(Attributes.EMAIL)))
889 // throw new NotAuthorizedException(StatusCodes.ILLEGAL_ARGUMENT,
890 // "invalid parameter: email", "email");
891 throw new WrappedException(
892 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "email invalid", email),
893 StatusCodes.PASSWORD_RESET_FAILED, email);
894 uritoken = crypto.encodeBase();
895 URIParam param = new URIParam(uritoken, TimeUtils.plusHours(24).getMillis());
896 user.addField(param);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200897
margarethaebc54962017-05-29 13:23:07 +0200898 try {
899 entHandler.updateAccount(user);
900 } catch (KustvaktException e) {
901 jlog.error("Error ", e.string());
902 throw new WrappedException(e, StatusCodes.PASSWORD_RESET_FAILED);
903 }
904 return new Object[] { uritoken, TimeUtils.format(param.getUriExpiration()) };
905 }
Michael Hanl87106d12015-09-14 18:13:51 +0200906
margaretha38d530e2017-07-11 19:06:50 +0200907 // EM: not in the new DB
margarethaebc54962017-05-29 13:23:07 +0200908 @Override
909 public <T extends Userdata> T getUserData(User user, Class<T> clazz) throws WrappedException {
margarethaebc54962017-05-29 13:23:07 +0200910 try {
911 UserDataDbIface<T> dao = BeansFactory.getTypeFactory()
912 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), clazz);
913 T data = null;
914 if (dao != null)
915 data = dao.get(user);
Michael Hanl87106d12015-09-14 18:13:51 +0200916
margarethaebc54962017-05-29 13:23:07 +0200917 if (data == null)
margarethaf6d5a822017-10-19 19:51:20 +0200918 throw new KustvaktException(user.getId(), StatusCodes.NO_RESULT_FOUND, "No data found!",
margarethaebc54962017-05-29 13:23:07 +0200919 clazz.getSimpleName());
920 return data;
921 } catch (KustvaktException e) {
922 jlog.error("Error during user data retrieval: {}", e.getEntity());
923 throw new WrappedException(e, StatusCodes.GET_ACCOUNT_FAILED);
924 }
925 }
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100926
margarethaebc54962017-05-29 13:23:07 +0200927 // todo: cache userdata outside of the user object!
928 @Override
929 public void updateUserData(Userdata data) throws WrappedException {
930 try {
931 data.validate(this.validator);
932 UserDataDbIface dao = BeansFactory.getTypeFactory()
933 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), data.getClass());
934 if (dao != null)
935 dao.update(data);
936 } catch (KustvaktException e) {
937 jlog.error("Error during update of user data!", e.getEntity());
938 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
939 }
940 }
margaretha2afb97d2017-12-07 19:18:44 +0100941
Michael Hanl87106d12015-09-14 18:13:51 +0200942}