blob: 73728a4df3938716664ffaef1361f685e07f875d [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;
margaretha4edc70e2018-03-14 22:34:29 +010017import org.springframework.beans.factory.annotation.Autowired;
margarethaebc54962017-05-29 13:23:07 +020018
19// import com.novell.ldap.*; search() funktioniert nicht korrekt, ausgewechselt gegen unboundID's Bibliothek 20.04.17/FB
20//Using JAR from unboundID:
21import com.unboundid.ldap.sdk.LDAPException;
Bodmoca3dcfb2017-05-24 16:36:00 +020022
Michael Hanl87106d12015-09-14 18:13:51 +020023import de.ids_mannheim.korap.auditing.AuditRecord;
Michael Hanl00b64e02016-05-24 20:24:27 +020024import de.ids_mannheim.korap.config.Attributes;
margaretha2afb97d2017-12-07 19:18:44 +010025import de.ids_mannheim.korap.config.AuthenticationMethod;
Michael Hanldaf86602016-05-12 14:31:52 +020026import de.ids_mannheim.korap.config.BeansFactory;
margaretha56e8e552017-12-05 16:31:21 +010027import de.ids_mannheim.korap.config.FullConfiguration;
margaretha2afb97d2017-12-07 19:18:44 +010028import de.ids_mannheim.korap.config.TokenType;
Michael Hanl87106d12015-09-14 18:13:51 +020029import de.ids_mannheim.korap.config.URIParam;
margaretha4edc70e2018-03-14 22:34:29 +010030import de.ids_mannheim.korap.dao.AdminDao;
margarethaebc54962017-05-29 13:23:07 +020031import de.ids_mannheim.korap.exceptions.EmptyResultException;
32import de.ids_mannheim.korap.exceptions.KustvaktException;
33import de.ids_mannheim.korap.exceptions.NotAuthorizedException;
34import de.ids_mannheim.korap.exceptions.StatusCodes;
35import de.ids_mannheim.korap.exceptions.WrappedException;
Michael Hanl19390652016-01-16 11:01:24 +010036import de.ids_mannheim.korap.interfaces.AuthenticationIface;
37import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
38import de.ids_mannheim.korap.interfaces.EncryptionIface;
margaretha69e8adc2018-03-15 15:14:22 +010039import de.ids_mannheim.korap.interfaces.EntityHandlerIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020040import de.ids_mannheim.korap.interfaces.ValidatorIface;
Michael Hanlf21773f2015-10-16 23:02:31 +020041import de.ids_mannheim.korap.interfaces.db.AuditingIface;
Michael Hanl415276b2016-01-29 16:39:37 +010042import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020043import de.ids_mannheim.korap.interfaces.defaults.ApacheValidator;
margarethaebc54962017-05-29 13:23:07 +020044import de.ids_mannheim.korap.user.DemoUser;
45import de.ids_mannheim.korap.user.KorAPUser;
46import de.ids_mannheim.korap.user.ShibUser;
47import de.ids_mannheim.korap.user.TokenContext;
48import de.ids_mannheim.korap.user.User;
Bodmoca3dcfb2017-05-24 16:36:00 +020049import de.ids_mannheim.korap.user.User.CorpusAccess;
margarethaebc54962017-05-29 13:23:07 +020050import de.ids_mannheim.korap.user.User.Location;
51import de.ids_mannheim.korap.user.UserDetails;
52import de.ids_mannheim.korap.user.UserSettings;
53import de.ids_mannheim.korap.user.Userdata;
Michael Hanl87106d12015-09-14 18:13:51 +020054import de.ids_mannheim.korap.utils.TimeUtils;
Bodmoca3dcfb2017-05-24 16:36:00 +020055
Michael Hanl87106d12015-09-14 18:13:51 +020056/**
margarethaebc54962017-05-29 13:23:07 +020057 * contains the logic to authentication and registration processes. Uses
58 * interface implementations (AuthenticationIface) for different databases and
59 * handlers
Michael Hanl8abaf9e2016-05-23 16:46:35 +020060 *
Michael Hanl87106d12015-09-14 18:13:51 +020061 * @author hanl
62 */
63public class KustvaktAuthenticationManager extends AuthenticationManagerIface {
64
margarethaebc54962017-05-29 13:23:07 +020065 private static Logger jlog = LoggerFactory.getLogger(KustvaktAuthenticationManager.class);
66 private EncryptionIface crypto;
67 private EntityHandlerIface entHandler;
margaretha4edc70e2018-03-14 22:34:29 +010068 @Autowired
69 private AdminDao adminDao;
margarethaebc54962017-05-29 13:23:07 +020070 private AuditingIface auditing;
margaretha56e8e552017-12-05 16:31:21 +010071 private FullConfiguration config;
margarethaebc54962017-05-29 13:23:07 +020072 private Collection userdatadaos;
73 private LoginCounter counter;
74 private ValidatorIface validator;
margaretha2afb97d2017-12-07 19:18:44 +010075
margaretha4edc70e2018-03-14 22:34:29 +010076 public KustvaktAuthenticationManager(EntityHandlerIface userdb, EncryptionIface crypto,
margaretha56e8e552017-12-05 16:31:21 +010077 FullConfiguration config, AuditingIface auditer, Collection<UserDataDbIface> userdatadaos) {
margarethaebc54962017-05-29 13:23:07 +020078 this.entHandler = userdb;
margarethaebc54962017-05-29 13:23:07 +020079 this.config = config;
80 this.crypto = crypto;
81 this.auditing = auditer;
82 this.counter = new LoginCounter(config);
83 this.userdatadaos = userdatadaos;
84 // todo: load via beancontext
85 try {
86 this.validator = new ApacheValidator();
87 } catch (IOException e) {
88 e.printStackTrace();
89 }
90 }
Michael Hanl87106d12015-09-14 18:13:51 +020091
margarethaebc54962017-05-29 13:23:07 +020092 /**
93 * get session object if token was a session token
94 *
95 * @param token
96 * @param host
97 * @param useragent
98 * @return
99 * @throws KustvaktException
100 */
101 @Override
margarethadfecb4b2017-12-12 19:32:30 +0100102 public TokenContext getTokenContext(TokenType type, String token,
margaretha4b5c1412017-11-15 20:55:04 +0100103 String host, String useragent) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200104
margaretha4de41192017-11-15 11:47:11 +0100105 AuthenticationIface provider = getProvider(type , null);
Michael Hanl99cb9632016-06-29 16:24:40 +0200106
margaretha6b3ecdd2018-03-01 18:23:56 +0100107 if (provider == null){
margaretha4b5c1412017-11-15 20:55:04 +0100108 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
margaretha6b3ecdd2018-03-01 18:23:56 +0100109 "Authentication provider for token type "+type
110 +" is not found.", type.displayName());
111 }
112
margarethaebc54962017-05-29 13:23:07 +0200113 TokenContext context = provider.getTokenContext(token);
margarethaebc54962017-05-29 13:23:07 +0200114 // if (!matchStatus(host, useragent, context))
115 // provider.removeUserSession(token);
116 return context;
117 }
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200118
margarethaebc54962017-05-29 13:23:07 +0200119 @Override
120 public User getUser(String username) throws KustvaktException {
121 // User user;
122 // Object value = this.getCacheValue(username);
Michael Hanl87106d12015-09-14 18:13:51 +0200123
margarethaebc54962017-05-29 13:23:07 +0200124 if (User.UserFactory.isDemo(username))
125 return User.UserFactory.getDemoUser();
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200126
margarethaebc54962017-05-29 13:23:07 +0200127 // if (value != null) {
128 // Map map = (Map) value;
129 // user = User.UserFactory.toUser(map);
130 // }
131 // else {
132 // user = entHandler.getAccount(username);
133 // this.storeInCache(username, user.toCache());
134 // todo: not valid. for the duration of the session, the host should not
135 // change!
136 // }
137 // todo:
138 // user.addField(Attributes.HOST, context.getHostAddress());
139 // user.addField(Attributes.USER_AGENT, context.getUserAgent());
margaretha0b63de42017-12-20 18:48:09 +0100140
141 //EM:copied from EntityDao
142 KorAPUser user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
143 user.setUsername(username);
144 return user;
145// return entHandler.getAccount(username);
margarethaebc54962017-05-29 13:23:07 +0200146 }
margaretha7d0165c2018-02-26 15:31:37 +0100147
148 @Override
149 public User getUser (String username, String method)
150 throws KustvaktException {
151 KorAPUser user = new KorAPUser();
152 user.setUsername(username);
153 String email = null;
154 switch (method.toLowerCase()) {
155 case "ldap":
156 email = config.getTestEmail();
157 break;
158 default:
159 email = config.getTestEmail();
160 break;
161 }
162 user.setEmail(email);
163 return user;
164 }
Michael Hanl87106d12015-09-14 18:13:51 +0200165
margarethaebc54962017-05-29 13:23:07 +0200166 public TokenContext refresh(TokenContext context) throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100167 AuthenticationIface provider = getProvider(context.getTokenType(), null);
margarethaebc54962017-05-29 13:23:07 +0200168 if (provider == null) {
169 // todo:
170 }
Michael Hanlc4446022016-02-12 18:03:17 +0100171
margarethaebc54962017-05-29 13:23:07 +0200172 try {
173 provider.removeUserSession(context.getToken());
174 User user = getUser(context.getUsername());
175 return provider.createTokenContext(user, context.params());
176 } catch (KustvaktException e) {
177 throw new WrappedException(e, StatusCodes.LOGIN_FAILED);
178 }
179 }
Michael Hanl87106d12015-09-14 18:13:51 +0200180
margarethafde771a2017-11-14 15:02:10 +0100181 /** EM: fix type is not flexible
margarethaebc54962017-05-29 13:23:07 +0200182 * @param type
183 * @param attributes
184 * contains username and password to authenticate the user.
185 * Depending of the authentication schema, may contain other
186 * values as well
187 * @return User
188 * @throws KustvaktException
189 */
190 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100191 public User authenticate(AuthenticationMethod method, String username, String password, Map<String, Object> attributes)
margarethaebc54962017-05-29 13:23:07 +0200192 throws KustvaktException {
193 User user;
margaretha2afb97d2017-12-07 19:18:44 +0100194 switch (method) {
margaretha139d0f72017-11-14 18:56:22 +0100195 case SHIBBOLETH:
margarethaebc54962017-05-29 13:23:07 +0200196 // todo:
197 user = authenticateShib(attributes);
198 break;
margaretha139d0f72017-11-14 18:56:22 +0100199 case LDAP:
margarethaebc54962017-05-29 13:23:07 +0200200 // IdM/LDAP: (09.02.17/FB)
201 user = authenticateIdM(username, password, attributes);
202 break;
203 default:
204 user = authenticate(username, password, attributes);
205 break;
206 }
207 auditing.audit(AuditRecord.serviceRecord(user.getId(), StatusCodes.LOGIN_SUCCESSFUL, user.toString()));
208 return user;
209 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200210
margarethaebc54962017-05-29 13:23:07 +0200211 // a. set location depending on X-Forwarded-For.
212 // X-Forwarded-For: clientIP, ProxyID, ProxyID...
213 // the following private address spaces may be used to define intranet
214 // spaces:
215 // 10.0.0.0 - 10.255.255.255 (10/8 prefix)
216 // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
217 // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
218 // b. set corpusAccess depending on location:
Bodmo11fb6f82017-06-01 11:39:15 +0200219 // c. DemoUser only gets corpusAccess=FREE.
margarethaebc54962017-05-29 13:23:07 +0200220 // 16.05.17/FB
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200221
margarethaebc54962017-05-29 13:23:07 +0200222 @Override
223 public void setAccessAndLocation(User user, HttpHeaders headers) {
margarethaebc54962017-05-29 13:23:07 +0200224 MultivaluedMap<String, String> headerMap = headers.getRequestHeaders();
225 Location location = Location.EXTERN;
226 CorpusAccess corpusAccess = CorpusAccess.FREE;
margarethaa89c3f92017-05-30 19:02:08 +0200227
Bodmo11fb6f82017-06-01 11:39:15 +0200228 if( user instanceof DemoUser )
229 {
230 // to be absolutely sure:
231 user.setCorpusAccess(User.CorpusAccess.FREE);
margarethaa86b1412018-02-21 20:40:35 +0100232 jlog.debug("setAccessAndLocation: DemoUser: location=%s, access=%s.\n", user.locationtoString(), user.accesstoString());
Bodmo11fb6f82017-06-01 11:39:15 +0200233 return;
234 }
margarethaa89c3f92017-05-30 19:02:08 +0200235
margaretha58e18632018-02-15 13:04:42 +0100236 if (headerMap != null && headerMap.containsKey(com.google.common.net.HttpHeaders.X_FORWARDED_FOR)) {
Michael Hanl87106d12015-09-14 18:13:51 +0200237
margaretha58e18632018-02-15 13:04:42 +0100238 String[] vals = headerMap.getFirst(com.google.common.net.HttpHeaders.X_FORWARDED_FOR).split(",");
margarethaebc54962017-05-29 13:23:07 +0200239 String clientAddress = vals[0];
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200240
margarethaebc54962017-05-29 13:23:07 +0200241 try {
242 InetAddress ip = InetAddress.getByName(clientAddress);
243 if (ip.isSiteLocalAddress()){
margarethaebc54962017-05-29 13:23:07 +0200244 location = Location.INTERN;
margarethaebc54962017-05-29 13:23:07 +0200245 corpusAccess = CorpusAccess.ALL;
246 }
margarethaa89c3f92017-05-30 19:02:08 +0200247 else{
248 corpusAccess = CorpusAccess.PUB;
249 }
250
margarethabb486302017-11-21 13:47:22 +0100251 jlog.debug(String.format("X-Forwarded-For : '%s' (%d values) -> %s\n",
252 Arrays.toString(vals), vals.length, vals[0]));
253 jlog.debug(String.format("X-Forwarded-For : location = %s corpusAccess = %s\n",
254 location == Location.INTERN ? "INTERN" : "EXTERN", corpusAccess == CorpusAccess.ALL ? "ALL"
255 : corpusAccess == CorpusAccess.PUB ? "PUB" : "FREE"));
Michael Hanl87106d12015-09-14 18:13:51 +0200256
margarethaebc54962017-05-29 13:23:07 +0200257 } catch (UnknownHostException e) {
258 // TODO Auto-generated catch block
259 e.printStackTrace();
260 }
Michael Hanl87106d12015-09-14 18:13:51 +0200261
margarethaebc54962017-05-29 13:23:07 +0200262 user.setLocation(location);
263 user.setCorpusAccess(corpusAccess);
margarethaa86b1412018-02-21 20:40:35 +0100264
265 jlog.debug("setAccessAndLocation: KorAPUser: location=%s, access=%s.\n", user.locationtoString(), user.accesstoString());
Bodmoc125bf12017-06-01 16:23:59 +0200266
margarethaebc54962017-05-29 13:23:07 +0200267 }
268 } // getAccess
Michael Hanl87106d12015-09-14 18:13:51 +0200269
margarethaebc54962017-05-29 13:23:07 +0200270 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100271 public TokenContext createTokenContext(User user, Map<String, Object> attr, TokenType type)
margarethaebc54962017-05-29 13:23:07 +0200272 throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100273 // use api token
274 AuthenticationIface provider = getProvider(type, TokenType.API);
Michael Hanl87106d12015-09-14 18:13:51 +0200275
margaretha38d530e2017-07-11 19:06:50 +0200276 // EM: not in the new DB
277// if (attr.get(Attributes.SCOPES) != null)
278// this.getUserData(user, UserDetails.class);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200279
margarethaebc54962017-05-29 13:23:07 +0200280 TokenContext context = provider.createTokenContext(user, attr);
281 if (context == null)
282 throw new KustvaktException(StatusCodes.NOT_SUPPORTED);
283 context.setUserAgent((String) attr.get(Attributes.USER_AGENT));
284 context.setHostAddress(Attributes.HOST);
285 return context;
286 }
Michael Hanl87106d12015-09-14 18:13:51 +0200287
margarethaebc54962017-05-29 13:23:07 +0200288 // todo: test
289 @Deprecated
290 private boolean matchStatus(String host, String useragent, TokenContext context) {
291 if (host.equals(context.getHostAddress())) {
292 if (useragent.equals(context.getUserAgent()))
293 return true;
294 }
295 return false;
296 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200297
margarethaebc54962017-05-29 13:23:07 +0200298 private User authenticateShib(Map<String, Object> attributes) throws KustvaktException {
299 // todo use persistent id, since eppn is not unique
300 String eppn = (String) attributes.get(Attributes.EPPN);
Michael Hanl87106d12015-09-14 18:13:51 +0200301
margarethaebc54962017-05-29 13:23:07 +0200302 if (eppn == null || eppn.isEmpty())
303 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200304
margarethaebc54962017-05-29 13:23:07 +0200305 if (!attributes.containsKey(Attributes.EMAIL) && validator.isValid(eppn, Attributes.EMAIL))
306 attributes.put(Attributes.EMAIL, eppn);
Michael Hanl87106d12015-09-14 18:13:51 +0200307
margarethaebc54962017-05-29 13:23:07 +0200308 User user = null;
309 if (isRegistered(eppn))
310 user = createShibbUserAccount(attributes);
311 return user;
312 }
Michael Hanl87106d12015-09-14 18:13:51 +0200313
margarethaebc54962017-05-29 13:23:07 +0200314 // todo: what if attributes null?
315 private User authenticate(String username, String password, Map<String, Object> attr) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200316
margarethaebc54962017-05-29 13:23:07 +0200317 Map<String, Object> attributes = validator.validateMap(attr);
318 User unknown;
319 // just to make sure that the plain password does not appear anywhere in
320 // the logs!
Michael Hanl87106d12015-09-14 18:13:51 +0200321
margarethaebc54962017-05-29 13:23:07 +0200322 try {
323 validator.validateEntry(username, Attributes.USERNAME);
324 } catch (KustvaktException e) {
325 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, username);
326 }
Michael Hanl87106d12015-09-14 18:13:51 +0200327
margarethaebc54962017-05-29 13:23:07 +0200328 if (username == null || username.isEmpty())
329 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
330 StatusCodes.LOGIN_FAILED);
331 else {
332 try {
333 unknown = entHandler.getAccount(username);
334 } catch (EmptyResultException e) {
335 // mask exception to disable user guessing in possible attacks
336 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
337 StatusCodes.LOGIN_FAILED, username);
338 } catch (KustvaktException e) {
339 jlog.error("Error: {}", e);
340 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, attributes.toString());
341 }
342 }
Michael Hanl87106d12015-09-14 18:13:51 +0200343
margaretha4edc70e2018-03-14 22:34:29 +0100344 boolean isAdmin = adminDao.isAdmin(unknown.getUsername());
margarethaf18298b2017-09-14 22:14:32 +0200345 jlog.debug("Authentication: found username " + unknown.getUsername());
Michael Hanl87106d12015-09-14 18:13:51 +0200346
margarethaebc54962017-05-29 13:23:07 +0200347 if (unknown instanceof KorAPUser) {
348 if (password == null || password.isEmpty())
349 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.BAD_CREDENTIALS),
350 StatusCodes.LOGIN_FAILED, username);
Michael Hanl87106d12015-09-14 18:13:51 +0200351
margarethaebc54962017-05-29 13:23:07 +0200352 KorAPUser user = (KorAPUser) unknown;
353 boolean check = crypto.checkHash(password, user.getPassword());
Michael Hanl87106d12015-09-14 18:13:51 +0200354
margarethaebc54962017-05-29 13:23:07 +0200355 if (!check) {
356 // the fail counter only applies for wrong password
357 jlog.warn("Wrong Password!");
358 processLoginFail(unknown);
359 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
360 StatusCodes.LOGIN_FAILED, username);
361 }
Michael Hanl87106d12015-09-14 18:13:51 +0200362
margarethaebc54962017-05-29 13:23:07 +0200363 // bad credentials error has precedence over account locked or
364 // unconfirmed codes
365 // since latter can lead to account guessing of third parties
366 if (user.isAccountLocked()) {
367 URIParam param = (URIParam) user.getField(URIParam.class);
Michael Hanl87106d12015-09-14 18:13:51 +0200368
margarethaebc54962017-05-29 13:23:07 +0200369 if (param.hasValues()) {
370 jlog.debug("Account is not yet activated for user '{}'", user.getUsername());
371 if (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
372 jlog.error("URI token is expired. Deleting account for user {}", user.getUsername());
373 deleteAccount(user);
374 throw new WrappedException(
375 new KustvaktException(unknown.getId(), StatusCodes.EXPIRED,
376 "account confirmation uri has expired!", param.getUriFragment()),
377 StatusCodes.LOGIN_FAILED, username);
378 }
379 throw new WrappedException(
380 new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
381 StatusCodes.LOGIN_FAILED, username);
382 }
383 jlog.error("ACCESS DENIED: account not active for '{}'", unknown.getUsername());
384 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
385 StatusCodes.LOGIN_FAILED, username);
386 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200387
margarethaebc54962017-05-29 13:23:07 +0200388 } else if (unknown instanceof ShibUser) {
389 // todo
390 }
margarethaf18298b2017-09-14 22:14:32 +0200391 jlog.debug("Authentication done: "+unknown);
margarethaebc54962017-05-29 13:23:07 +0200392 return unknown;
393 }
394
395 /**
396 * authenticate using IdM (Identitätsmanagement) accessed by LDAP.
397 *
398 * @param username
399 * @param password
400 * @param attr
401 * @return
402 * @throws KustvaktException
403 * @date 09.02.17/FB
404 */
405 // todo: what if attributes null?
Bodmo3d6bd352017-04-25 11:31:39 +0200406
margarethaebc54962017-05-29 13:23:07 +0200407 private User authenticateIdM(String username, String password, Map<String, Object> attr) throws KustvaktException {
Bodmo3d6bd352017-04-25 11:31:39 +0200408
margarethaebc54962017-05-29 13:23:07 +0200409 Map<String, Object> attributes = validator.validateMap(attr);
410 User unknown = null;
411 // just to make sure that the plain password does not appear anywhere in
412 // the logs!
Bodmo3d6bd352017-04-25 11:31:39 +0200413
margarethaebc54962017-05-29 13:23:07 +0200414 System.out.printf("Debug: authenticateIdM: entering for '%s'...\n", username);
Bodmo3d6bd352017-04-25 11:31:39 +0200415
margarethaebc54962017-05-29 13:23:07 +0200416 /**
417 * wozu Apache Validatoren für User/Passwort für IdM/LDAP? siehe
418 * validation.properties. Abgeschaltet 21.04.17/FB try {
419 * validator.validateEntry(username, Attributes.USERNAME); } catch
420 * (KustvaktException e) { throw new WrappedException(e,
421 * StatusCodes.LOGIN_FAILED, username); }
422 */
423 if (username == null || username.isEmpty() || password == null || password.isEmpty())
424 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
425 StatusCodes.LOGIN_FAILED);
Bodmo3d6bd352017-04-25 11:31:39 +0200426
margarethaebc54962017-05-29 13:23:07 +0200427 // LDAP Access:
428 try {
429 // todo: unknown = ...
margaretha65b67142017-05-29 16:23:16 +0200430 int ret = LdapAuth3.login(username, password, config.getLdapConfig());
margarethaebc54962017-05-29 13:23:07 +0200431 System.out.printf("Debug: autenticationIdM: Ldap.login(%s) returns: %d.\n", username, ret);
432 if (ret != LdapAuth3.LDAP_AUTH_ROK) {
433 jlog.error("LdapAuth3.login(username='{}') returns '{}'='{}'!", username, ret,
434 LdapAuth3.getErrMessage(ret));
Bodmo3d6bd352017-04-25 11:31:39 +0200435
margarethaebc54962017-05-29 13:23:07 +0200436 // mask exception to disable user guessing in possible attacks
437 /*
438 * by Hanl throw new WrappedException(new
439 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
440 * StatusCodes.LOGIN_FAILED, username);
441 */
442 throw new WrappedException(new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + ret,
443 LdapAuth3.getErrMessage(ret), null), StatusCodes.LOGIN_FAILED, username);
444 }
445 } catch (LDAPException e) {
Bodmo3d6bd352017-04-25 11:31:39 +0200446
margarethaebc54962017-05-29 13:23:07 +0200447 jlog.error("Error: username='{}' -> '{}'!", username, e);
448 // mask exception to disable user guessing in possible attacks
449 /*
450 * by Hanl: throw new WrappedException(new
451 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
452 * StatusCodes.LOGIN_FAILED, username);
453 */
454 throw new WrappedException(
455 new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + LdapAuth3.LDAP_AUTH_RINTERR,
456 LdapAuth3.getErrMessage(LdapAuth3.LDAP_AUTH_RINTERR), null),
457 StatusCodes.LOGIN_FAILED, username);
458 }
Bodmo3d6bd352017-04-25 11:31:39 +0200459
margarethaebc54962017-05-29 13:23:07 +0200460 // Create a User
461 // TODO: KorAPUser für solche mit einem bestehenden Account
462 // DefaultUser sonst.
463 User user = new KorAPUser();
464 user.setUsername(username);
465 /*
466 * folgender Code funktioniert hier noch nicht, da die Headers noch
467 * nicht ausgewertet worden sind - 23.05.17/FB Object o =
468 * attr.get(Attributes.LOCATION); String loc = (String)o.toString(); int
469 * location = Integer.parseInt(loc); user.setLocation(location);
470 * user.setCorpusAccess(Integer.parseInt(attr.get(Attributes.
471 * CORPUS_ACCESS).toString()));
472 */
473 unknown = user;
Michael Hanl87106d12015-09-14 18:13:51 +0200474
margarethaebc54962017-05-29 13:23:07 +0200475 jlog.trace("Authentication: found username " + unknown.getUsername());
Michael Hanl87106d12015-09-14 18:13:51 +0200476
margarethaebc54962017-05-29 13:23:07 +0200477 if (unknown instanceof KorAPUser) {
478 /*
479 * password already checked using LDAP: if (password == null ||
480 * password.isEmpty()) throw new WrappedException(new
481 * KustvaktException( unknown.getId(), StatusCodes.BAD_CREDENTIALS),
482 * StatusCodes.LOGIN_FAILED, username);
483 *
484 * KorAPUser user = (KorAPUser) unknown; boolean check =
485 * crypto.checkHash(password, user.getPassword());
486 *
487 * if (!check) { // the fail counter only applies for wrong password
488 * jlog.warn("Wrong Password!"); processLoginFail(unknown); throw
489 * new WrappedException(new KustvaktException(user.getId(),
490 * StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
491 * username); }
492 */
493 // bad credentials error has precedence over account locked or
494 // unconfirmed codes
495 // since latter can lead to account guessing of third parties
496 /*
497 * if (user.isAccountLocked()) {
498 *
499 * URIParam param = (URIParam) user.getField(URIParam.class);
500 *
501 * if (param.hasValues()) {
502 * jlog.debug("Account is not yet activated for user '{}'",
503 * user.getUsername()); if
504 * (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
505 * jlog.error( "URI token is expired. Deleting account for user {}",
506 * user.getUsername()); deleteAccount(user); throw new
507 * WrappedException(new KustvaktException( unknown.getId(),
508 * StatusCodes.EXPIRED, "account confirmation uri has expired!",
509 * param.getUriFragment()), StatusCodes.LOGIN_FAILED, username); }
510 * throw new WrappedException(new KustvaktException(
511 * unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
512 * StatusCodes.LOGIN_FAILED, username); }
513 * jlog.error("ACCESS DENIED: account not active for '{}'",
514 * unknown.getUsername()); throw new WrappedException(new
515 * KustvaktException( unknown.getId(),
516 * StatusCodes.ACCOUNT_DEACTIVATED), StatusCodes.LOGIN_FAILED,
517 * username); }
518 */
Michael Hanl87106d12015-09-14 18:13:51 +0200519
margarethaebc54962017-05-29 13:23:07 +0200520 } else if (unknown instanceof ShibUser) {
521 // todo
522 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200523
margarethaebc54962017-05-29 13:23:07 +0200524 jlog.debug("Authentication done: " + username);
525 return unknown;
Michael Hanl19390652016-01-16 11:01:24 +0100526
margarethaebc54962017-05-29 13:23:07 +0200527 } // authenticateIdM
Michael Hanl87106d12015-09-14 18:13:51 +0200528
margarethaebc54962017-05-29 13:23:07 +0200529 public boolean isRegistered(String username) {
530 User user;
531 if (username == null || username.isEmpty())
532 return false;
533 // throw new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT,
534 // "username must be set", username);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200535
margarethaebc54962017-05-29 13:23:07 +0200536 try {
537 user = entHandler.getAccount(username);
538 } catch (EmptyResultException e) {
539 jlog.debug("user does not exist ({})", username);
540 return false;
Michael Hanl87106d12015-09-14 18:13:51 +0200541
margarethaebc54962017-05-29 13:23:07 +0200542 } catch (KustvaktException e) {
543 jlog.error("KorAPException", e.string());
544 return false;
545 // throw new KustvaktException(username,
546 // StatusCodes.ILLEGAL_ARGUMENT,
547 // "username invalid", username);
548 }
549 return user != null;
550 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200551
margarethaebc54962017-05-29 13:23:07 +0200552 public void logout(TokenContext context) throws KustvaktException {
553 try {
margaretha2afb97d2017-12-07 19:18:44 +0100554 AuthenticationIface provider = getProvider(context.getTokenType(), null);
Michael Hanl87106d12015-09-14 18:13:51 +0200555
margarethaebc54962017-05-29 13:23:07 +0200556 if (provider == null) {
margaretha4de41192017-11-15 11:47:11 +0100557 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "Authentication "
margaretha2afb97d2017-12-07 19:18:44 +0100558 + "provider not supported!", context.getTokenType().displayName());
margarethaebc54962017-05-29 13:23:07 +0200559 }
560 provider.removeUserSession(context.getToken());
561 } catch (KustvaktException e) {
562 throw new WrappedException(e, StatusCodes.LOGOUT_FAILED, context.toString());
563 }
564 auditing.audit(
565 AuditRecord.serviceRecord(context.getUsername(), StatusCodes.LOGOUT_SUCCESSFUL, context.toString()));
566 this.removeCacheEntry(context.getToken());
567 }
Michael Hanl87106d12015-09-14 18:13:51 +0200568
margarethaebc54962017-05-29 13:23:07 +0200569 private void processLoginFail(User user) throws KustvaktException {
570 counter.registerFail(user.getUsername());
571 if (!counter.validate(user.getUsername())) {
572 try {
573 this.lockAccount(user);
574 } catch (KustvaktException e) {
575 jlog.error("user account could not be locked", e);
576 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
577 }
578 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
579 StatusCodes.LOGIN_FAILED);
580 }
581 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200582
margarethaebc54962017-05-29 13:23:07 +0200583 public void lockAccount(User user) throws KustvaktException {
584 if (!(user instanceof KorAPUser))
585 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200586
margarethaebc54962017-05-29 13:23:07 +0200587 KorAPUser u = (KorAPUser) user;
588 u.setAccountLocked(true);
589 jlog.info("locking account for user: {}", user.getUsername());
590 entHandler.updateAccount(u);
591 }
Michael Hanl87106d12015-09-14 18:13:51 +0200592
margarethaebc54962017-05-29 13:23:07 +0200593 public KorAPUser checkPasswordAllowance(KorAPUser user, String oldPassword, String newPassword)
594 throws KustvaktException {
595 String dbPassword = user.getPassword();
Michael Hanl87106d12015-09-14 18:13:51 +0200596
margarethaebc54962017-05-29 13:23:07 +0200597 if (oldPassword.trim().equals(newPassword.trim())) {
598 // TODO: special error StatusCodes for this?
599 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT),
600 StatusCodes.PASSWORD_RESET_FAILED, newPassword);
601 }
Michael Hanl87106d12015-09-14 18:13:51 +0200602
margarethaebc54962017-05-29 13:23:07 +0200603 boolean check = crypto.checkHash(oldPassword, dbPassword);
Michael Hanl87106d12015-09-14 18:13:51 +0200604
margarethaebc54962017-05-29 13:23:07 +0200605 if (!check)
606 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
607 StatusCodes.PASSWORD_RESET_FAILED);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200608
margarethaebc54962017-05-29 13:23:07 +0200609 try {
610 user.setPassword(crypto.secureHash(newPassword));
611 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
612 throw new WrappedException(
613 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassword),
614 StatusCodes.PASSWORD_RESET_FAILED, user.toString(), newPassword);
615 }
616 return user;
617 }
Michael Hanl87106d12015-09-14 18:13:51 +0200618
margarethaebc54962017-05-29 13:23:07 +0200619 // fixme: use clientinfo for logging/auditing?! = from where did he access
620 // the reset function?
621 @Override
622 public void resetPassword(String uriFragment, String username, String newPassphrase) throws KustvaktException {
623 try {
624 validator.validateEntry(username, Attributes.USERNAME);
625 validator.validateEntry(newPassphrase, Attributes.PASSWORD);
626 } catch (KustvaktException e) {
627 jlog.error("Error: {}", e.string());
628 throw new WrappedException(
629 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
630 StatusCodes.PASSWORD_RESET_FAILED, username, newPassphrase);
631 }
Michael Hanl87106d12015-09-14 18:13:51 +0200632
margarethaebc54962017-05-29 13:23:07 +0200633 try {
634 newPassphrase = crypto.secureHash(newPassphrase);
635 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
636 jlog.error("Encoding/Algorithm Error", e);
637 throw new WrappedException(
638 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
639 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment, newPassphrase);
640 }
641 int result = entHandler.resetPassphrase(username, uriFragment, newPassphrase);
Michael Hanl87106d12015-09-14 18:13:51 +0200642
margarethaebc54962017-05-29 13:23:07 +0200643 if (result == 0)
644 throw new WrappedException(
645 new KustvaktException(username, StatusCodes.EXPIRED, "URI fragment expired", uriFragment),
646 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment);
647 else if (result == 1)
648 jlog.info("successfully reset password for user {}", username);
649 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200650
margarethaebc54962017-05-29 13:23:07 +0200651 public void confirmRegistration(String uriFragment, String username) throws KustvaktException {
652 try {
653 validator.validateEntry(username, Attributes.USERNAME);
654 } catch (KustvaktException e) {
655 jlog.error("Error: {}", e.string());
656 throw new WrappedException(e, StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
657 }
658 int r = entHandler.activateAccount(username, uriFragment);
659 if (r == 0) {
660 User user;
661 try {
662 user = entHandler.getAccount(username);
663 } catch (EmptyResultException e) {
664 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
665 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
666 }
667 entHandler.deleteAccount(user.getId());
668 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.EXPIRED),
669 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
670 } else if (r == 1)
671 jlog.info("successfully confirmed user registration for user {}", username);
672 // register successful audit!
673 }
Michael Hanl87106d12015-09-14 18:13:51 +0200674
margarethaebc54962017-05-29 13:23:07 +0200675 /**
676 * @param attributes
677 * @return
678 * @throws KustvaktException
679 */
680 // fixme: remove clientinfo object (not needed), use json representation to
681 // get stuff
682 public User createUserAccount(Map<String, Object> attributes, boolean confirmation_required)
683 throws KustvaktException {
684 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200685
margarethaebc54962017-05-29 13:23:07 +0200686 if (safeMap.get(Attributes.USERNAME) == null || ((String) safeMap.get(Attributes.USERNAME)).isEmpty())
687 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "username must be set", "username");
688 if (safeMap.get(Attributes.PASSWORD) == null || ((String) safeMap.get(Attributes.PASSWORD)).isEmpty())
689 throw new KustvaktException(safeMap.get(Attributes.USERNAME), StatusCodes.ILLEGAL_ARGUMENT,
690 "password must be set", "password");
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200691
margarethaebc54962017-05-29 13:23:07 +0200692 String username = validator.validateEntry((String) safeMap.get(Attributes.USERNAME), Attributes.USERNAME);
693 String password = validator.validateEntry((String) safeMap.get(Attributes.PASSWORD), Attributes.PASSWORD);
694 String hash;
695 try {
696 hash = crypto.secureHash(password);
697 } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
698 jlog.error("Encryption error", e);
699 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT);
700 }
Michael Hanl87106d12015-09-14 18:13:51 +0200701
margarethaebc54962017-05-29 13:23:07 +0200702 KorAPUser user = User.UserFactory.getUser(username);
703 Object id = attributes.get(Attributes.ID);
704 if (id != null && id instanceof Integer)
705 user.setId((Integer) id);
Michael Hanl87106d12015-09-14 18:13:51 +0200706
margarethaebc54962017-05-29 13:23:07 +0200707 user.setAccountLocked(confirmation_required);
708 if (confirmation_required) {
709 URIParam param = new URIParam(crypto.createToken(),
710 TimeUtils.plusSeconds(config.getTokenTTL()).getMillis());
711 user.addField(param);
712 }
713 user.setPassword(hash);
Michael Hanl7368aa42016-02-05 18:15:47 +0100714
margaretha4edc70e2018-03-14 22:34:29 +0100715// String o = (String) attributes.get(Attributes.IS_ADMIN);
716// boolean b = Boolean.parseBoolean(o);
717// user.setSystemAdmin(b);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200718
margarethaebc54962017-05-29 13:23:07 +0200719 try {
720 UserDetails details = new UserDetails();
721 details.read(safeMap, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200722
margarethaebc54962017-05-29 13:23:07 +0200723 UserSettings settings = new UserSettings();
724 settings.read(safeMap, true);
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100725
margarethaebc54962017-05-29 13:23:07 +0200726 jlog.info("Creating new user account for user {}", user.getUsername());
727 entHandler.createAccount(user);
margaretha4edc70e2018-03-14 22:34:29 +0100728// if (user.isSystemAdmin() && user instanceof KorAPUser) {
729// adminDao.addAccount(user);
730// user.setCorpusAccess(CorpusAccess.ALL);
731// }
margarethaebc54962017-05-29 13:23:07 +0200732 details.setUserId(user.getId());
733 settings.setUserId(user.getId());
Michael Hanl87106d12015-09-14 18:13:51 +0200734
margarethaebc54962017-05-29 13:23:07 +0200735 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
736 // todo: remove this
737 assert dao != null;
738 dao.store(details);
739 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
740 assert dao != null;
741 dao.store(settings);
742 } catch (KustvaktException e) {
743 jlog.error("Error: {}", e.string());
744 throw new WrappedException(e, StatusCodes.CREATE_ACCOUNT_FAILED, user.toString());
745 }
Michael Hanl87106d12015-09-14 18:13:51 +0200746
margarethaebc54962017-05-29 13:23:07 +0200747 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.CREATE_ACCOUNT_SUCCESSFUL));
748 return user;
749 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200750
margarethaebc54962017-05-29 13:23:07 +0200751 // todo:
752 private ShibUser createShibbUserAccount(Map<String, Object> attributes) throws KustvaktException {
753 jlog.debug("creating shibboleth user account for user attr: {}", attributes);
754 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl87106d12015-09-14 18:13:51 +0200755
margarethaebc54962017-05-29 13:23:07 +0200756 // todo eppn non-unique.join with idp or use persistent_id as username
757 // identifier
758 ShibUser user = User.UserFactory.getShibInstance((String) safeMap.get(Attributes.EPPN),
759 (String) safeMap.get(Attributes.MAIL), (String) safeMap.get(Attributes.CN));
760 user.setAffiliation((String) safeMap.get(Attributes.EDU_AFFIL));
761 user.setAccountCreation(TimeUtils.getNow().getMillis());
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200762
margarethaebc54962017-05-29 13:23:07 +0200763 UserDetails d = new UserDetails();
764 d.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200765
margarethaebc54962017-05-29 13:23:07 +0200766 UserSettings s = new UserSettings();
767 s.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200768
margarethaebc54962017-05-29 13:23:07 +0200769 entHandler.createAccount(user);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200770
margarethaebc54962017-05-29 13:23:07 +0200771 s.setUserId(user.getId());
772 d.setUserId(user.getId());
Michael Hanl25aac542016-02-01 18:16:44 +0100773
margarethaebc54962017-05-29 13:23:07 +0200774 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
775 assert dao != null;
776 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100777
margarethaebc54962017-05-29 13:23:07 +0200778 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
779 assert dao != null;
780 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100781
margarethaebc54962017-05-29 13:23:07 +0200782 return user;
783 }
Michael Hanl25aac542016-02-01 18:16:44 +0100784
margarethaebc54962017-05-29 13:23:07 +0200785 /**
786 * link shibboleth and korap user account to one another.
787 *
788 * @param current
789 * currently logged in user
790 * @param for_name
791 * foreign user name the current account should be linked to
792 * @param transstrat
793 * transfer status of user data (details, settings, user queries)
794 * 0 = the currently logged in data should be kept 1 = the
795 * foreign account data should be kept
796 * @throws NotAuthorizedException
797 * @throws KustvaktException
798 */
799 // todo:
800 public void accountLink(User current, String for_name, int transstrat) throws KustvaktException {
801 // User foreign = entHandler.getAccount(for_name);
Michael Hanl87106d12015-09-14 18:13:51 +0200802
margarethaebc54962017-05-29 13:23:07 +0200803 // if (current.getAccountLink() == null && current.getAccountLink()
804 // .isEmpty()) {
805 // if (current instanceof KorAPUser && foreign instanceof ShibUser) {
806 // if (transstrat == 1)
807 // current.transfer(foreign);
808 //// foreign.setAccountLink(current.getUsername());
809 //// current.setAccountLink(foreign.getUsername());
810 // // entHandler.purgeDetails(foreign);
811 // // entHandler.purgeSettings(foreign);
812 // }else if (foreign instanceof KorAPUser
813 // && current instanceof ShibUser) {
814 // if (transstrat == 0)
815 // foreign.transfer(current);
816 //// current.setAccountLink(foreign.getUsername());
817 // // entHandler.purgeDetails(current);
818 // // entHandler.purgeSettings(current);
819 // // entHandler.purgeSettings(current);
820 // }
821 // entHandler.updateAccount(current);
822 // entHandler.updateAccount(foreign);
823 // }
824 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200825
margarethaebc54962017-05-29 13:23:07 +0200826 // todo: test and rest usage?!
827 public boolean updateAccount(User user) throws KustvaktException {
828 boolean result;
829 if (user instanceof DemoUser)
830 throw new KustvaktException(user.getId(), StatusCodes.REQUEST_INVALID,
831 "account not updateable for demo user", user.getUsername());
832 else {
833 // crypto.validate(user);
834 try {
835 result = entHandler.updateAccount(user) > 0;
836 } catch (KustvaktException e) {
837 jlog.error("Error: {}", e.string());
838 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
839 }
840 }
841 if (result) {
842 // this.removeCacheEntry(user.getUsername());
843 auditing.audit(
844 AuditRecord.serviceRecord(user.getId(), StatusCodes.UPDATE_ACCOUNT_SUCCESSFUL, user.toString()));
845 }
846 return result;
847 }
Michael Hanl87106d12015-09-14 18:13:51 +0200848
margarethaebc54962017-05-29 13:23:07 +0200849 public boolean deleteAccount(User user) throws KustvaktException {
850 boolean result;
851 if (user instanceof DemoUser)
852 return true;
853 else {
854 try {
855 result = entHandler.deleteAccount(user.getId()) > 0;
856 } catch (KustvaktException e) {
857 jlog.error("Error: {}", e.string());
858 throw new WrappedException(e, StatusCodes.DELETE_ACCOUNT_FAILED);
859 }
860 }
861 if (result) {
862 // this.removeCacheEntry(user.getUsername());
863 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.DELETE_ACCOUNT_SUCCESSFUL,
864 user.toString()));
865 }
866 return result;
867 }
Michael Hanl87106d12015-09-14 18:13:51 +0200868
margarethaebc54962017-05-29 13:23:07 +0200869 public Object[] validateResetPasswordRequest(String username, String email) throws KustvaktException {
870 String uritoken;
871 validator.validateEntry(email, Attributes.EMAIL);
872 User ident;
873 try {
874 ident = entHandler.getAccount(username);
875 if (ident instanceof DemoUser)
876 // throw new
877 // NotAuthorizedException(StatusCodes.PERMISSION_DENIED,
878 // "password reset now allowed for DemoUser", "");
879 throw new WrappedException(username, StatusCodes.PASSWORD_RESET_FAILED, username);
880 } catch (EmptyResultException e) {
881 throw new WrappedException(
882 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "username not found", username),
883 StatusCodes.PASSWORD_RESET_FAILED, username);
884 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200885
margarethaebc54962017-05-29 13:23:07 +0200886 Userdata data = this.getUserData(ident, UserDetails.class);
887 KorAPUser user = (KorAPUser) ident;
Michael Hanl87106d12015-09-14 18:13:51 +0200888
margarethaebc54962017-05-29 13:23:07 +0200889 if (!email.equals(data.get(Attributes.EMAIL)))
890 // throw new NotAuthorizedException(StatusCodes.ILLEGAL_ARGUMENT,
891 // "invalid parameter: email", "email");
892 throw new WrappedException(
893 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "email invalid", email),
894 StatusCodes.PASSWORD_RESET_FAILED, email);
895 uritoken = crypto.encodeBase();
896 URIParam param = new URIParam(uritoken, TimeUtils.plusHours(24).getMillis());
897 user.addField(param);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200898
margarethaebc54962017-05-29 13:23:07 +0200899 try {
900 entHandler.updateAccount(user);
901 } catch (KustvaktException e) {
902 jlog.error("Error ", e.string());
903 throw new WrappedException(e, StatusCodes.PASSWORD_RESET_FAILED);
904 }
905 return new Object[] { uritoken, TimeUtils.format(param.getUriExpiration()) };
906 }
Michael Hanl87106d12015-09-14 18:13:51 +0200907
margaretha38d530e2017-07-11 19:06:50 +0200908 // EM: not in the new DB
margarethaebc54962017-05-29 13:23:07 +0200909 @Override
910 public <T extends Userdata> T getUserData(User user, Class<T> clazz) throws WrappedException {
margarethaebc54962017-05-29 13:23:07 +0200911 try {
912 UserDataDbIface<T> dao = BeansFactory.getTypeFactory()
913 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), clazz);
914 T data = null;
915 if (dao != null)
916 data = dao.get(user);
Michael Hanl87106d12015-09-14 18:13:51 +0200917
margarethaebc54962017-05-29 13:23:07 +0200918 if (data == null)
margarethaf6d5a822017-10-19 19:51:20 +0200919 throw new KustvaktException(user.getId(), StatusCodes.NO_RESULT_FOUND, "No data found!",
margarethaebc54962017-05-29 13:23:07 +0200920 clazz.getSimpleName());
921 return data;
922 } catch (KustvaktException e) {
923 jlog.error("Error during user data retrieval: {}", e.getEntity());
924 throw new WrappedException(e, StatusCodes.GET_ACCOUNT_FAILED);
925 }
926 }
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100927
margarethaebc54962017-05-29 13:23:07 +0200928 // todo: cache userdata outside of the user object!
929 @Override
930 public void updateUserData(Userdata data) throws WrappedException {
931 try {
932 data.validate(this.validator);
933 UserDataDbIface dao = BeansFactory.getTypeFactory()
934 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), data.getClass());
935 if (dao != null)
936 dao.update(data);
937 } catch (KustvaktException e) {
938 jlog.error("Error during update of user data!", e.getEntity());
939 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
940 }
941 }
margaretha2afb97d2017-12-07 19:18:44 +0100942
Michael Hanl87106d12015-09-14 18:13:51 +0200943}