blob: e6bce21d60aca781db2819d0d7cc6687491955bf [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
margaretha49cb6882018-07-04 04:19:54 +020015import org.apache.logging.log4j.LogManager;
16import org.apache.logging.log4j.Logger;
margaretha4edc70e2018-03-14 22:34:29 +010017import org.springframework.beans.factory.annotation.Autowired;
margarethaebc54962017-05-29 13:23:07 +020018
margaretha6ef00dd2018-09-12 14:06:38 +020019import com.mchange.rmi.NotAuthorizedException;
margarethaebc54962017-05-29 13:23:07 +020020// import com.novell.ldap.*; search() funktioniert nicht korrekt, ausgewechselt gegen unboundID's Bibliothek 20.04.17/FB
21//Using JAR from unboundID:
22import com.unboundid.ldap.sdk.LDAPException;
Bodmoca3dcfb2017-05-24 16:36:00 +020023
Michael Hanl87106d12015-09-14 18:13:51 +020024import de.ids_mannheim.korap.auditing.AuditRecord;
Michael Hanl00b64e02016-05-24 20:24:27 +020025import de.ids_mannheim.korap.config.Attributes;
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;
Michael Hanl87106d12015-09-14 18:13:51 +020028import de.ids_mannheim.korap.config.URIParam;
margaretha0e8f4e72018-04-05 14:11:52 +020029import de.ids_mannheim.korap.constant.AuthenticationMethod;
30import de.ids_mannheim.korap.constant.TokenType;
margaretha4edc70e2018-03-14 22:34:29 +010031import de.ids_mannheim.korap.dao.AdminDao;
margarethaebc54962017-05-29 13:23:07 +020032import de.ids_mannheim.korap.exceptions.EmptyResultException;
33import de.ids_mannheim.korap.exceptions.KustvaktException;
margarethaebc54962017-05-29 13:23:07 +020034import 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.EncryptionIface;
margaretha69e8adc2018-03-15 15:14:22 +010037import de.ids_mannheim.korap.interfaces.EntityHandlerIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020038import de.ids_mannheim.korap.interfaces.ValidatorIface;
Michael Hanlf21773f2015-10-16 23:02:31 +020039import de.ids_mannheim.korap.interfaces.db.AuditingIface;
Michael Hanl415276b2016-01-29 16:39:37 +010040import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020041import de.ids_mannheim.korap.interfaces.defaults.ApacheValidator;
margaretha0e8f4e72018-04-05 14:11:52 +020042import de.ids_mannheim.korap.security.context.TokenContext;
margarethaebc54962017-05-29 13:23:07 +020043import de.ids_mannheim.korap.user.DemoUser;
44import de.ids_mannheim.korap.user.KorAPUser;
margarethafb027f92018-04-23 20:00:13 +020045import de.ids_mannheim.korap.user.ShibbolethUser;
margarethaebc54962017-05-29 13:23:07 +020046import de.ids_mannheim.korap.user.User;
Bodmoca3dcfb2017-05-24 16:36:00 +020047import de.ids_mannheim.korap.user.User.CorpusAccess;
margarethaebc54962017-05-29 13:23:07 +020048import de.ids_mannheim.korap.user.User.Location;
49import de.ids_mannheim.korap.user.UserDetails;
50import de.ids_mannheim.korap.user.UserSettings;
51import de.ids_mannheim.korap.user.Userdata;
Michael Hanl87106d12015-09-14 18:13:51 +020052import de.ids_mannheim.korap.utils.TimeUtils;
Bodmoca3dcfb2017-05-24 16:36:00 +020053
Michael Hanl87106d12015-09-14 18:13:51 +020054/**
margarethaebc54962017-05-29 13:23:07 +020055 * contains the logic to authentication and registration processes. Uses
56 * interface implementations (AuthenticationIface) for different databases and
57 * handlers
Michael Hanl8abaf9e2016-05-23 16:46:35 +020058 *
Michael Hanl87106d12015-09-14 18:13:51 +020059 * @author hanl
60 */
margaretha34954472018-10-24 20:05:17 +020061public class KustvaktAuthenticationManager extends AuthenticationManager {
Michael Hanl87106d12015-09-14 18:13:51 +020062
margaretha49cb6882018-07-04 04:19:54 +020063 private static Logger jlog = LogManager.getLogger(KustvaktAuthenticationManager.class);
margarethadda4ef72018-12-06 14:20:51 +010064 public static boolean DEBUG = false;
65
margarethaebc54962017-05-29 13:23:07 +020066 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) {
margaretha2df06602018-11-14 19:10:30 +010078 super("id_tokens");
margarethaebc54962017-05-29 13:23:07 +020079 this.entHandler = userdb;
margarethaebc54962017-05-29 13:23:07 +020080 this.config = config;
81 this.crypto = crypto;
82 this.auditing = auditer;
83 this.counter = new LoginCounter(config);
84 this.userdatadaos = userdatadaos;
85 // todo: load via beancontext
86 try {
87 this.validator = new ApacheValidator();
88 } catch (IOException e) {
89 e.printStackTrace();
90 }
91 }
Michael Hanl87106d12015-09-14 18:13:51 +020092
margarethaebc54962017-05-29 13:23:07 +020093 /**
94 * get session object if token was a session token
95 *
96 * @param token
97 * @param host
98 * @param useragent
99 * @return
100 * @throws KustvaktException
101 */
102 @Override
margarethadfecb4b2017-12-12 19:32:30 +0100103 public TokenContext getTokenContext(TokenType type, String token,
margaretha4b5c1412017-11-15 20:55:04 +0100104 String host, String useragent) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200105
margaretha4de41192017-11-15 11:47:11 +0100106 AuthenticationIface provider = getProvider(type , null);
Michael Hanl99cb9632016-06-29 16:24:40 +0200107
margaretha6b3ecdd2018-03-01 18:23:56 +0100108 if (provider == null){
margaretha4b5c1412017-11-15 20:55:04 +0100109 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
margaretha6b3ecdd2018-03-01 18:23:56 +0100110 "Authentication provider for token type "+type
111 +" is not found.", type.displayName());
112 }
113
margarethaebc54962017-05-29 13:23:07 +0200114 TokenContext context = provider.getTokenContext(token);
margarethaebc54962017-05-29 13:23:07 +0200115 // if (!matchStatus(host, useragent, context))
116 // provider.removeUserSession(token);
117 return context;
118 }
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200119
margarethaebc54962017-05-29 13:23:07 +0200120 @Override
121 public User getUser(String username) throws KustvaktException {
122 // User user;
123 // Object value = this.getCacheValue(username);
Michael Hanl87106d12015-09-14 18:13:51 +0200124
margarethaebc54962017-05-29 13:23:07 +0200125 if (User.UserFactory.isDemo(username))
126 return User.UserFactory.getDemoUser();
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200127
margarethaebc54962017-05-29 13:23:07 +0200128 // if (value != null) {
129 // Map map = (Map) value;
130 // user = User.UserFactory.toUser(map);
131 // }
132 // else {
133 // user = entHandler.getAccount(username);
134 // this.storeInCache(username, user.toCache());
135 // todo: not valid. for the duration of the session, the host should not
136 // change!
137 // }
138 // todo:
139 // user.addField(Attributes.HOST, context.getHostAddress());
140 // user.addField(Attributes.USER_AGENT, context.getUserAgent());
margaretha0b63de42017-12-20 18:48:09 +0100141
142 //EM:copied from EntityDao
143 KorAPUser user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
144 user.setUsername(username);
145 return user;
146// return entHandler.getAccount(username);
margarethaebc54962017-05-29 13:23:07 +0200147 }
margaretha7d0165c2018-02-26 15:31:37 +0100148
149 @Override
150 public User getUser (String username, String method)
151 throws KustvaktException {
152 KorAPUser user = new KorAPUser();
153 user.setUsername(username);
154 String email = null;
155 switch (method.toLowerCase()) {
156 case "ldap":
157 email = config.getTestEmail();
158 break;
159 default:
160 email = config.getTestEmail();
161 break;
162 }
163 user.setEmail(email);
164 return user;
165 }
Michael Hanl87106d12015-09-14 18:13:51 +0200166
margarethaebc54962017-05-29 13:23:07 +0200167 public TokenContext refresh(TokenContext context) throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100168 AuthenticationIface provider = getProvider(context.getTokenType(), null);
margarethaebc54962017-05-29 13:23:07 +0200169 if (provider == null) {
170 // todo:
171 }
Michael Hanlc4446022016-02-12 18:03:17 +0100172
margarethaebc54962017-05-29 13:23:07 +0200173 try {
174 provider.removeUserSession(context.getToken());
175 User user = getUser(context.getUsername());
176 return provider.createTokenContext(user, context.params());
177 } catch (KustvaktException e) {
178 throw new WrappedException(e, StatusCodes.LOGIN_FAILED);
179 }
180 }
Michael Hanl87106d12015-09-14 18:13:51 +0200181
margarethafde771a2017-11-14 15:02:10 +0100182 /** EM: fix type is not flexible
margarethaebc54962017-05-29 13:23:07 +0200183 * @param type
184 * @param attributes
185 * contains username and password to authenticate the user.
186 * Depending of the authentication schema, may contain other
187 * values as well
188 * @return User
189 * @throws KustvaktException
190 */
191 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100192 public User authenticate(AuthenticationMethod method, String username, String password, Map<String, Object> attributes)
margarethaebc54962017-05-29 13:23:07 +0200193 throws KustvaktException {
194 User user;
margaretha2afb97d2017-12-07 19:18:44 +0100195 switch (method) {
margaretha139d0f72017-11-14 18:56:22 +0100196 case SHIBBOLETH:
margarethaebc54962017-05-29 13:23:07 +0200197 // todo:
198 user = authenticateShib(attributes);
199 break;
margaretha139d0f72017-11-14 18:56:22 +0100200 case LDAP:
margarethaebc54962017-05-29 13:23:07 +0200201 // IdM/LDAP: (09.02.17/FB)
202 user = authenticateIdM(username, password, attributes);
203 break;
margaretha6374f722018-04-17 18:45:57 +0200204 // EM: added a dummy authentication for testing
205 case TEST:
206 user = getUser(username);
207 break;
margarethaebc54962017-05-29 13:23:07 +0200208 default:
209 user = authenticate(username, password, attributes);
210 break;
211 }
212 auditing.audit(AuditRecord.serviceRecord(user.getId(), StatusCodes.LOGIN_SUCCESSFUL, user.toString()));
213 return user;
214 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200215
margarethaebc54962017-05-29 13:23:07 +0200216 // a. set location depending on X-Forwarded-For.
217 // X-Forwarded-For: clientIP, ProxyID, ProxyID...
218 // the following private address spaces may be used to define intranet
219 // spaces:
220 // 10.0.0.0 - 10.255.255.255 (10/8 prefix)
221 // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
222 // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
223 // b. set corpusAccess depending on location:
Bodmo11fb6f82017-06-01 11:39:15 +0200224 // c. DemoUser only gets corpusAccess=FREE.
margarethaebc54962017-05-29 13:23:07 +0200225 // 16.05.17/FB
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200226
margarethaebc54962017-05-29 13:23:07 +0200227 @Override
228 public void setAccessAndLocation(User user, HttpHeaders headers) {
margarethaebc54962017-05-29 13:23:07 +0200229 MultivaluedMap<String, String> headerMap = headers.getRequestHeaders();
230 Location location = Location.EXTERN;
231 CorpusAccess corpusAccess = CorpusAccess.FREE;
margarethaa89c3f92017-05-30 19:02:08 +0200232
Bodmo11fb6f82017-06-01 11:39:15 +0200233 if( user instanceof DemoUser )
234 {
235 // to be absolutely sure:
236 user.setCorpusAccess(User.CorpusAccess.FREE);
margarethadda4ef72018-12-06 14:20:51 +0100237 if (DEBUG) {
238 jlog.debug("setAccessAndLocation: DemoUser: location="
239 + user.locationtoString() + " access="
240 + user.accesstoString());
241 }
Bodmo11fb6f82017-06-01 11:39:15 +0200242 return;
243 }
margarethaa89c3f92017-05-30 19:02:08 +0200244
margaretha58e18632018-02-15 13:04:42 +0100245 if (headerMap != null && headerMap.containsKey(com.google.common.net.HttpHeaders.X_FORWARDED_FOR)) {
Michael Hanl87106d12015-09-14 18:13:51 +0200246
margaretha58e18632018-02-15 13:04:42 +0100247 String[] vals = headerMap.getFirst(com.google.common.net.HttpHeaders.X_FORWARDED_FOR).split(",");
margarethaebc54962017-05-29 13:23:07 +0200248 String clientAddress = vals[0];
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200249
margarethaebc54962017-05-29 13:23:07 +0200250 try {
251 InetAddress ip = InetAddress.getByName(clientAddress);
252 if (ip.isSiteLocalAddress()){
margarethaebc54962017-05-29 13:23:07 +0200253 location = Location.INTERN;
margarethaebc54962017-05-29 13:23:07 +0200254 corpusAccess = CorpusAccess.ALL;
255 }
margarethaa89c3f92017-05-30 19:02:08 +0200256 else{
257 corpusAccess = CorpusAccess.PUB;
258 }
259
margarethadda4ef72018-12-06 14:20:51 +0100260 if (DEBUG){
261 jlog.debug(String.format(
262 "X-Forwarded-For : '%s' (%d values) -> %s\n",
263 Arrays.toString(vals), vals.length, vals[0]));
264 jlog.debug(String.format(
265 "X-Forwarded-For : location = %s corpusAccess = %s\n",
266 location == Location.INTERN ? "INTERN" : "EXTERN",
267 corpusAccess == CorpusAccess.ALL ? "ALL"
268 : corpusAccess == CorpusAccess.PUB ? "PUB"
269 : "FREE"));
270 }
Michael Hanl87106d12015-09-14 18:13:51 +0200271
margarethaebc54962017-05-29 13:23:07 +0200272 } catch (UnknownHostException e) {
273 // TODO Auto-generated catch block
274 e.printStackTrace();
275 }
Michael Hanl87106d12015-09-14 18:13:51 +0200276
margarethaebc54962017-05-29 13:23:07 +0200277 user.setLocation(location);
278 user.setCorpusAccess(corpusAccess);
margarethaa86b1412018-02-21 20:40:35 +0100279
margarethadda4ef72018-12-06 14:20:51 +0100280 if (DEBUG) {
281 jlog.debug("setAccessAndLocation: KorAPUser: location="
282 + user.locationtoString() + ", access="
283 + user.accesstoString());
284 }
Bodmoc125bf12017-06-01 16:23:59 +0200285
margarethaebc54962017-05-29 13:23:07 +0200286 }
287 } // getAccess
Michael Hanl87106d12015-09-14 18:13:51 +0200288
margarethaebc54962017-05-29 13:23:07 +0200289 @Override
margaretha2afb97d2017-12-07 19:18:44 +0100290 public TokenContext createTokenContext(User user, Map<String, Object> attr, TokenType type)
margarethaebc54962017-05-29 13:23:07 +0200291 throws KustvaktException {
margaretha2afb97d2017-12-07 19:18:44 +0100292 // use api token
293 AuthenticationIface provider = getProvider(type, TokenType.API);
Michael Hanl87106d12015-09-14 18:13:51 +0200294
margaretha38d530e2017-07-11 19:06:50 +0200295 // EM: not in the new DB
296// if (attr.get(Attributes.SCOPES) != null)
297// this.getUserData(user, UserDetails.class);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200298
margarethaebc54962017-05-29 13:23:07 +0200299 TokenContext context = provider.createTokenContext(user, attr);
300 if (context == null)
301 throw new KustvaktException(StatusCodes.NOT_SUPPORTED);
302 context.setUserAgent((String) attr.get(Attributes.USER_AGENT));
303 context.setHostAddress(Attributes.HOST);
304 return context;
305 }
Michael Hanl87106d12015-09-14 18:13:51 +0200306
margarethaebc54962017-05-29 13:23:07 +0200307 // todo: test
308 @Deprecated
309 private boolean matchStatus(String host, String useragent, TokenContext context) {
310 if (host.equals(context.getHostAddress())) {
311 if (useragent.equals(context.getUserAgent()))
312 return true;
313 }
314 return false;
315 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200316
margarethaebc54962017-05-29 13:23:07 +0200317 private User authenticateShib(Map<String, Object> attributes) throws KustvaktException {
318 // todo use persistent id, since eppn is not unique
319 String eppn = (String) attributes.get(Attributes.EPPN);
Michael Hanl87106d12015-09-14 18:13:51 +0200320
margarethaebc54962017-05-29 13:23:07 +0200321 if (eppn == null || eppn.isEmpty())
322 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200323
margarethaebc54962017-05-29 13:23:07 +0200324 if (!attributes.containsKey(Attributes.EMAIL) && validator.isValid(eppn, Attributes.EMAIL))
325 attributes.put(Attributes.EMAIL, eppn);
Michael Hanl87106d12015-09-14 18:13:51 +0200326
margarethaebc54962017-05-29 13:23:07 +0200327 User user = null;
328 if (isRegistered(eppn))
329 user = createShibbUserAccount(attributes);
330 return user;
331 }
Michael Hanl87106d12015-09-14 18:13:51 +0200332
margarethaebc54962017-05-29 13:23:07 +0200333 // todo: what if attributes null?
334 private User authenticate(String username, String password, Map<String, Object> attr) throws KustvaktException {
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200335
margarethaebc54962017-05-29 13:23:07 +0200336 Map<String, Object> attributes = validator.validateMap(attr);
337 User unknown;
338 // just to make sure that the plain password does not appear anywhere in
339 // the logs!
Michael Hanl87106d12015-09-14 18:13:51 +0200340
margarethaebc54962017-05-29 13:23:07 +0200341 try {
342 validator.validateEntry(username, Attributes.USERNAME);
343 } catch (KustvaktException e) {
344 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, username);
345 }
Michael Hanl87106d12015-09-14 18:13:51 +0200346
margarethaebc54962017-05-29 13:23:07 +0200347 if (username == null || username.isEmpty())
348 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
349 StatusCodes.LOGIN_FAILED);
350 else {
351 try {
352 unknown = entHandler.getAccount(username);
353 } catch (EmptyResultException e) {
354 // mask exception to disable user guessing in possible attacks
355 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
356 StatusCodes.LOGIN_FAILED, username);
357 } catch (KustvaktException e) {
358 jlog.error("Error: {}", e);
359 throw new WrappedException(e, StatusCodes.LOGIN_FAILED, attributes.toString());
360 }
361 }
Michael Hanl87106d12015-09-14 18:13:51 +0200362
margaretha4edc70e2018-03-14 22:34:29 +0100363 boolean isAdmin = adminDao.isAdmin(unknown.getUsername());
margarethadda4ef72018-12-06 14:20:51 +0100364 if (DEBUG) {
365 jlog.debug(
366 "Authentication: found username " + unknown.getUsername());
367 }
margarethaebc54962017-05-29 13:23:07 +0200368 if (unknown instanceof KorAPUser) {
369 if (password == null || password.isEmpty())
370 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.BAD_CREDENTIALS),
371 StatusCodes.LOGIN_FAILED, username);
Michael Hanl87106d12015-09-14 18:13:51 +0200372
margarethaebc54962017-05-29 13:23:07 +0200373 KorAPUser user = (KorAPUser) unknown;
374 boolean check = crypto.checkHash(password, user.getPassword());
Michael Hanl87106d12015-09-14 18:13:51 +0200375
margarethaebc54962017-05-29 13:23:07 +0200376 if (!check) {
377 // the fail counter only applies for wrong password
378 jlog.warn("Wrong Password!");
379 processLoginFail(unknown);
380 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
381 StatusCodes.LOGIN_FAILED, username);
382 }
Michael Hanl87106d12015-09-14 18:13:51 +0200383
margarethaebc54962017-05-29 13:23:07 +0200384 // bad credentials error has precedence over account locked or
385 // unconfirmed codes
386 // since latter can lead to account guessing of third parties
387 if (user.isAccountLocked()) {
388 URIParam param = (URIParam) user.getField(URIParam.class);
Michael Hanl87106d12015-09-14 18:13:51 +0200389
margarethaebc54962017-05-29 13:23:07 +0200390 if (param.hasValues()) {
margarethadda4ef72018-12-06 14:20:51 +0100391 if (DEBUG) {
392 jlog.debug("Account is not yet activated for user '"
393 + user.getUsername() + "'");
394 }
margarethaebc54962017-05-29 13:23:07 +0200395 if (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
margaretha49cb6882018-07-04 04:19:54 +0200396 jlog.error("URI token is expired. Deleting account for user "+ user.getUsername());
margarethaebc54962017-05-29 13:23:07 +0200397 deleteAccount(user);
398 throw new WrappedException(
399 new KustvaktException(unknown.getId(), StatusCodes.EXPIRED,
400 "account confirmation uri has expired!", param.getUriFragment()),
401 StatusCodes.LOGIN_FAILED, username);
402 }
403 throw new WrappedException(
404 new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
405 StatusCodes.LOGIN_FAILED, username);
406 }
margaretha49cb6882018-07-04 04:19:54 +0200407 jlog.error("ACCESS DENIED: account not active for '"+unknown.getUsername()+"'");
margarethaebc54962017-05-29 13:23:07 +0200408 throw new WrappedException(new KustvaktException(unknown.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
409 StatusCodes.LOGIN_FAILED, username);
410 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200411
margarethafb027f92018-04-23 20:00:13 +0200412 } else if (unknown instanceof ShibbolethUser) {
margarethaebc54962017-05-29 13:23:07 +0200413 // todo
414 }
margarethadda4ef72018-12-06 14:20:51 +0100415 if (DEBUG) {
416 jlog.debug("Authentication done: " + unknown);
417 }
margarethaebc54962017-05-29 13:23:07 +0200418 return unknown;
419 }
420
421 /**
422 * authenticate using IdM (Identitätsmanagement) accessed by LDAP.
423 *
424 * @param username
425 * @param password
426 * @param attr
427 * @return
428 * @throws KustvaktException
429 * @date 09.02.17/FB
430 */
431 // todo: what if attributes null?
Bodmo3d6bd352017-04-25 11:31:39 +0200432
margarethaebc54962017-05-29 13:23:07 +0200433 private User authenticateIdM(String username, String password, Map<String, Object> attr) throws KustvaktException {
Bodmo3d6bd352017-04-25 11:31:39 +0200434
margarethaebc54962017-05-29 13:23:07 +0200435 Map<String, Object> attributes = validator.validateMap(attr);
436 User unknown = null;
437 // just to make sure that the plain password does not appear anywhere in
438 // the logs!
Bodmo3d6bd352017-04-25 11:31:39 +0200439
margarethaebc54962017-05-29 13:23:07 +0200440 System.out.printf("Debug: authenticateIdM: entering for '%s'...\n", username);
Bodmo3d6bd352017-04-25 11:31:39 +0200441
margarethaebc54962017-05-29 13:23:07 +0200442 /**
443 * wozu Apache Validatoren für User/Passwort für IdM/LDAP? siehe
444 * validation.properties. Abgeschaltet 21.04.17/FB try {
445 * validator.validateEntry(username, Attributes.USERNAME); } catch
446 * (KustvaktException e) { throw new WrappedException(e,
447 * StatusCodes.LOGIN_FAILED, username); }
448 */
449 if (username == null || username.isEmpty() || password == null || password.isEmpty())
450 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
451 StatusCodes.LOGIN_FAILED);
Bodmo3d6bd352017-04-25 11:31:39 +0200452
margarethaebc54962017-05-29 13:23:07 +0200453 // LDAP Access:
454 try {
455 // todo: unknown = ...
margaretha65b67142017-05-29 16:23:16 +0200456 int ret = LdapAuth3.login(username, password, config.getLdapConfig());
margarethadda4ef72018-12-06 14:20:51 +0100457 if (DEBUG){
458 jlog.debug("Debug: autenticationIdM: Ldap.login(%s) returns: %d.\n", username, ret);
459 }
margarethaebc54962017-05-29 13:23:07 +0200460 if (ret != LdapAuth3.LDAP_AUTH_ROK) {
margaretha49cb6882018-07-04 04:19:54 +0200461 jlog.error("LdapAuth3.login(username='"+username+"') returns '"+ret+"'='"+LdapAuth3.getErrMessage(ret)+"'!");
Bodmo3d6bd352017-04-25 11:31:39 +0200462
margarethaebc54962017-05-29 13:23:07 +0200463 // mask exception to disable user guessing in possible attacks
464 /*
465 * by Hanl throw new WrappedException(new
466 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
467 * StatusCodes.LOGIN_FAILED, username);
468 */
469 throw new WrappedException(new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + ret,
470 LdapAuth3.getErrMessage(ret), null), StatusCodes.LOGIN_FAILED, username);
471 }
472 } catch (LDAPException e) {
Bodmo3d6bd352017-04-25 11:31:39 +0200473
margaretha49cb6882018-07-04 04:19:54 +0200474 jlog.error("Error: username='"+username+"' -> '"+e+"'!");
margarethaebc54962017-05-29 13:23:07 +0200475 // mask exception to disable user guessing in possible attacks
476 /*
477 * by Hanl: throw new WrappedException(new
478 * KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
479 * StatusCodes.LOGIN_FAILED, username);
480 */
481 throw new WrappedException(
482 new KustvaktException(username, StatusCodes.LDAP_BASE_ERRCODE + LdapAuth3.LDAP_AUTH_RINTERR,
483 LdapAuth3.getErrMessage(LdapAuth3.LDAP_AUTH_RINTERR), null),
484 StatusCodes.LOGIN_FAILED, username);
485 }
Bodmo3d6bd352017-04-25 11:31:39 +0200486
margarethaebc54962017-05-29 13:23:07 +0200487 // Create a User
488 // TODO: KorAPUser für solche mit einem bestehenden Account
489 // DefaultUser sonst.
490 User user = new KorAPUser();
491 user.setUsername(username);
492 /*
493 * folgender Code funktioniert hier noch nicht, da die Headers noch
494 * nicht ausgewertet worden sind - 23.05.17/FB Object o =
495 * attr.get(Attributes.LOCATION); String loc = (String)o.toString(); int
496 * location = Integer.parseInt(loc); user.setLocation(location);
497 * user.setCorpusAccess(Integer.parseInt(attr.get(Attributes.
498 * CORPUS_ACCESS).toString()));
499 */
500 unknown = user;
Michael Hanl87106d12015-09-14 18:13:51 +0200501
margarethadda4ef72018-12-06 14:20:51 +0100502 if (DEBUG) {
503 jlog.trace(
504 "Authentication: found username " + unknown.getUsername());
505 }
margarethaebc54962017-05-29 13:23:07 +0200506 if (unknown instanceof KorAPUser) {
507 /*
508 * password already checked using LDAP: if (password == null ||
509 * password.isEmpty()) throw new WrappedException(new
510 * KustvaktException( unknown.getId(), StatusCodes.BAD_CREDENTIALS),
511 * StatusCodes.LOGIN_FAILED, username);
512 *
513 * KorAPUser user = (KorAPUser) unknown; boolean check =
514 * crypto.checkHash(password, user.getPassword());
515 *
516 * if (!check) { // the fail counter only applies for wrong password
517 * jlog.warn("Wrong Password!"); processLoginFail(unknown); throw
518 * new WrappedException(new KustvaktException(user.getId(),
519 * StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
520 * username); }
521 */
522 // bad credentials error has precedence over account locked or
523 // unconfirmed codes
524 // since latter can lead to account guessing of third parties
525 /*
526 * if (user.isAccountLocked()) {
527 *
528 * URIParam param = (URIParam) user.getField(URIParam.class);
529 *
530 * if (param.hasValues()) {
531 * jlog.debug("Account is not yet activated for user '{}'",
532 * user.getUsername()); if
533 * (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
534 * jlog.error( "URI token is expired. Deleting account for user {}",
535 * user.getUsername()); deleteAccount(user); throw new
536 * WrappedException(new KustvaktException( unknown.getId(),
537 * StatusCodes.EXPIRED, "account confirmation uri has expired!",
538 * param.getUriFragment()), StatusCodes.LOGIN_FAILED, username); }
539 * throw new WrappedException(new KustvaktException(
540 * unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
541 * StatusCodes.LOGIN_FAILED, username); }
542 * jlog.error("ACCESS DENIED: account not active for '{}'",
543 * unknown.getUsername()); throw new WrappedException(new
544 * KustvaktException( unknown.getId(),
545 * StatusCodes.ACCOUNT_DEACTIVATED), StatusCodes.LOGIN_FAILED,
546 * username); }
547 */
Michael Hanl87106d12015-09-14 18:13:51 +0200548
margarethafb027f92018-04-23 20:00:13 +0200549 } else if (unknown instanceof ShibbolethUser) {
margarethaebc54962017-05-29 13:23:07 +0200550 // todo
551 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200552
margarethadda4ef72018-12-06 14:20:51 +0100553 if (DEBUG) {
554 jlog.debug("Authentication done: " + username);
555 }
margarethaebc54962017-05-29 13:23:07 +0200556 return unknown;
Michael Hanl19390652016-01-16 11:01:24 +0100557
margarethaebc54962017-05-29 13:23:07 +0200558 } // authenticateIdM
Michael Hanl87106d12015-09-14 18:13:51 +0200559
margarethaebc54962017-05-29 13:23:07 +0200560 public boolean isRegistered(String username) {
561 User user;
562 if (username == null || username.isEmpty())
563 return false;
564 // throw new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT,
565 // "username must be set", username);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200566
margarethaebc54962017-05-29 13:23:07 +0200567 try {
568 user = entHandler.getAccount(username);
569 } catch (EmptyResultException e) {
margaretha49cb6882018-07-04 04:19:54 +0200570 jlog.debug("user does not exist: "+ username);
margarethaebc54962017-05-29 13:23:07 +0200571 return false;
Michael Hanl87106d12015-09-14 18:13:51 +0200572
margarethaebc54962017-05-29 13:23:07 +0200573 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200574 jlog.error("KorAPException "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200575 return false;
576 // throw new KustvaktException(username,
577 // StatusCodes.ILLEGAL_ARGUMENT,
578 // "username invalid", username);
579 }
580 return user != null;
581 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200582
margarethaebc54962017-05-29 13:23:07 +0200583 public void logout(TokenContext context) throws KustvaktException {
584 try {
margaretha2afb97d2017-12-07 19:18:44 +0100585 AuthenticationIface provider = getProvider(context.getTokenType(), null);
Michael Hanl87106d12015-09-14 18:13:51 +0200586
margarethaebc54962017-05-29 13:23:07 +0200587 if (provider == null) {
margaretha4de41192017-11-15 11:47:11 +0100588 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "Authentication "
margaretha2afb97d2017-12-07 19:18:44 +0100589 + "provider not supported!", context.getTokenType().displayName());
margarethaebc54962017-05-29 13:23:07 +0200590 }
591 provider.removeUserSession(context.getToken());
592 } catch (KustvaktException e) {
593 throw new WrappedException(e, StatusCodes.LOGOUT_FAILED, context.toString());
594 }
595 auditing.audit(
596 AuditRecord.serviceRecord(context.getUsername(), StatusCodes.LOGOUT_SUCCESSFUL, context.toString()));
597 this.removeCacheEntry(context.getToken());
598 }
Michael Hanl87106d12015-09-14 18:13:51 +0200599
margarethaebc54962017-05-29 13:23:07 +0200600 private void processLoginFail(User user) throws KustvaktException {
601 counter.registerFail(user.getUsername());
602 if (!counter.validate(user.getUsername())) {
603 try {
604 this.lockAccount(user);
605 } catch (KustvaktException e) {
606 jlog.error("user account could not be locked", e);
607 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
608 }
609 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
610 StatusCodes.LOGIN_FAILED);
611 }
612 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200613
margarethaebc54962017-05-29 13:23:07 +0200614 public void lockAccount(User user) throws KustvaktException {
615 if (!(user instanceof KorAPUser))
616 throw new KustvaktException(StatusCodes.REQUEST_INVALID);
Michael Hanl87106d12015-09-14 18:13:51 +0200617
margarethaebc54962017-05-29 13:23:07 +0200618 KorAPUser u = (KorAPUser) user;
619 u.setAccountLocked(true);
margaretha49cb6882018-07-04 04:19:54 +0200620 jlog.info("locking account for user: "+ user.getUsername());
margarethaebc54962017-05-29 13:23:07 +0200621 entHandler.updateAccount(u);
622 }
Michael Hanl87106d12015-09-14 18:13:51 +0200623
margarethaebc54962017-05-29 13:23:07 +0200624 public KorAPUser checkPasswordAllowance(KorAPUser user, String oldPassword, String newPassword)
625 throws KustvaktException {
626 String dbPassword = user.getPassword();
Michael Hanl87106d12015-09-14 18:13:51 +0200627
margarethaebc54962017-05-29 13:23:07 +0200628 if (oldPassword.trim().equals(newPassword.trim())) {
629 // TODO: special error StatusCodes for this?
630 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT),
631 StatusCodes.PASSWORD_RESET_FAILED, newPassword);
632 }
Michael Hanl87106d12015-09-14 18:13:51 +0200633
margarethaebc54962017-05-29 13:23:07 +0200634 boolean check = crypto.checkHash(oldPassword, dbPassword);
Michael Hanl87106d12015-09-14 18:13:51 +0200635
margarethaebc54962017-05-29 13:23:07 +0200636 if (!check)
637 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.BAD_CREDENTIALS),
638 StatusCodes.PASSWORD_RESET_FAILED);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200639
margarethaebc54962017-05-29 13:23:07 +0200640 try {
641 user.setPassword(crypto.secureHash(newPassword));
642 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
643 throw new WrappedException(
644 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassword),
645 StatusCodes.PASSWORD_RESET_FAILED, user.toString(), newPassword);
646 }
647 return user;
648 }
Michael Hanl87106d12015-09-14 18:13:51 +0200649
margarethaebc54962017-05-29 13:23:07 +0200650 // fixme: use clientinfo for logging/auditing?! = from where did he access
651 // the reset function?
652 @Override
653 public void resetPassword(String uriFragment, String username, String newPassphrase) throws KustvaktException {
654 try {
655 validator.validateEntry(username, Attributes.USERNAME);
656 validator.validateEntry(newPassphrase, Attributes.PASSWORD);
657 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200658 jlog.error("Error: "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200659 throw new WrappedException(
660 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
661 StatusCodes.PASSWORD_RESET_FAILED, username, newPassphrase);
662 }
Michael Hanl87106d12015-09-14 18:13:51 +0200663
margarethaebc54962017-05-29 13:23:07 +0200664 try {
665 newPassphrase = crypto.secureHash(newPassphrase);
666 } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
667 jlog.error("Encoding/Algorithm Error", e);
668 throw new WrappedException(
669 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "password invalid", newPassphrase),
670 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment, newPassphrase);
671 }
672 int result = entHandler.resetPassphrase(username, uriFragment, newPassphrase);
Michael Hanl87106d12015-09-14 18:13:51 +0200673
margarethaebc54962017-05-29 13:23:07 +0200674 if (result == 0)
675 throw new WrappedException(
676 new KustvaktException(username, StatusCodes.EXPIRED, "URI fragment expired", uriFragment),
677 StatusCodes.PASSWORD_RESET_FAILED, username, uriFragment);
678 else if (result == 1)
margaretha49cb6882018-07-04 04:19:54 +0200679 jlog.info("successfully reset password for user "+ username);
margarethaebc54962017-05-29 13:23:07 +0200680 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200681
margarethaebc54962017-05-29 13:23:07 +0200682 public void confirmRegistration(String uriFragment, String username) throws KustvaktException {
683 try {
684 validator.validateEntry(username, Attributes.USERNAME);
685 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200686 jlog.error("Error: "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200687 throw new WrappedException(e, StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
688 }
689 int r = entHandler.activateAccount(username, uriFragment);
690 if (r == 0) {
691 User user;
692 try {
693 user = entHandler.getAccount(username);
694 } catch (EmptyResultException e) {
695 throw new WrappedException(new KustvaktException(username, StatusCodes.BAD_CREDENTIALS),
696 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
697 }
698 entHandler.deleteAccount(user.getId());
699 throw new WrappedException(new KustvaktException(user.getId(), StatusCodes.EXPIRED),
700 StatusCodes.ACCOUNT_CONFIRMATION_FAILED, username, uriFragment);
701 } else if (r == 1)
margaretha49cb6882018-07-04 04:19:54 +0200702 jlog.info("successfully confirmed user registration for user "+ username);
margarethaebc54962017-05-29 13:23:07 +0200703 // register successful audit!
704 }
Michael Hanl87106d12015-09-14 18:13:51 +0200705
margarethaebc54962017-05-29 13:23:07 +0200706 /**
707 * @param attributes
708 * @return
709 * @throws KustvaktException
710 */
711 // fixme: remove clientinfo object (not needed), use json representation to
712 // get stuff
713 public User createUserAccount(Map<String, Object> attributes, boolean confirmation_required)
714 throws KustvaktException {
715 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200716
margarethaebc54962017-05-29 13:23:07 +0200717 if (safeMap.get(Attributes.USERNAME) == null || ((String) safeMap.get(Attributes.USERNAME)).isEmpty())
718 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT, "username must be set", "username");
719 if (safeMap.get(Attributes.PASSWORD) == null || ((String) safeMap.get(Attributes.PASSWORD)).isEmpty())
720 throw new KustvaktException(safeMap.get(Attributes.USERNAME), StatusCodes.ILLEGAL_ARGUMENT,
721 "password must be set", "password");
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200722
margarethaebc54962017-05-29 13:23:07 +0200723 String username = validator.validateEntry((String) safeMap.get(Attributes.USERNAME), Attributes.USERNAME);
724 String password = validator.validateEntry((String) safeMap.get(Attributes.PASSWORD), Attributes.PASSWORD);
725 String hash;
726 try {
727 hash = crypto.secureHash(password);
728 } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
729 jlog.error("Encryption error", e);
730 throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT);
731 }
Michael Hanl87106d12015-09-14 18:13:51 +0200732
margarethaebc54962017-05-29 13:23:07 +0200733 KorAPUser user = User.UserFactory.getUser(username);
734 Object id = attributes.get(Attributes.ID);
735 if (id != null && id instanceof Integer)
736 user.setId((Integer) id);
Michael Hanl87106d12015-09-14 18:13:51 +0200737
margarethaebc54962017-05-29 13:23:07 +0200738 user.setAccountLocked(confirmation_required);
739 if (confirmation_required) {
740 URIParam param = new URIParam(crypto.createToken(),
741 TimeUtils.plusSeconds(config.getTokenTTL()).getMillis());
742 user.addField(param);
743 }
744 user.setPassword(hash);
Michael Hanl7368aa42016-02-05 18:15:47 +0100745
margaretha4edc70e2018-03-14 22:34:29 +0100746// String o = (String) attributes.get(Attributes.IS_ADMIN);
747// boolean b = Boolean.parseBoolean(o);
748// user.setSystemAdmin(b);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200749
margarethaebc54962017-05-29 13:23:07 +0200750 try {
751 UserDetails details = new UserDetails();
752 details.read(safeMap, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200753
margarethaebc54962017-05-29 13:23:07 +0200754 UserSettings settings = new UserSettings();
755 settings.read(safeMap, true);
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100756
margaretha49cb6882018-07-04 04:19:54 +0200757 jlog.info("Creating new user account for user "+ user.getUsername());
margarethaebc54962017-05-29 13:23:07 +0200758 entHandler.createAccount(user);
margaretha4edc70e2018-03-14 22:34:29 +0100759// if (user.isSystemAdmin() && user instanceof KorAPUser) {
760// adminDao.addAccount(user);
761// user.setCorpusAccess(CorpusAccess.ALL);
762// }
margarethaebc54962017-05-29 13:23:07 +0200763 details.setUserId(user.getId());
764 settings.setUserId(user.getId());
Michael Hanl87106d12015-09-14 18:13:51 +0200765
margarethaebc54962017-05-29 13:23:07 +0200766 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
767 // todo: remove this
768 assert dao != null;
769 dao.store(details);
770 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
771 assert dao != null;
772 dao.store(settings);
773 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200774 jlog.error("Error: "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200775 throw new WrappedException(e, StatusCodes.CREATE_ACCOUNT_FAILED, user.toString());
776 }
Michael Hanl87106d12015-09-14 18:13:51 +0200777
margarethaebc54962017-05-29 13:23:07 +0200778 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.CREATE_ACCOUNT_SUCCESSFUL));
779 return user;
780 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200781
margarethaebc54962017-05-29 13:23:07 +0200782 // todo:
margarethafb027f92018-04-23 20:00:13 +0200783 private ShibbolethUser createShibbUserAccount(Map<String, Object> attributes) throws KustvaktException {
margarethadda4ef72018-12-06 14:20:51 +0100784 if (DEBUG) {
785 jlog.debug("creating shibboleth user account for user attr: "
786 + attributes);
787 }
margarethaebc54962017-05-29 13:23:07 +0200788 Map<String, Object> safeMap = validator.validateMap(attributes);
Michael Hanl87106d12015-09-14 18:13:51 +0200789
margarethaebc54962017-05-29 13:23:07 +0200790 // todo eppn non-unique.join with idp or use persistent_id as username
791 // identifier
margarethafb027f92018-04-23 20:00:13 +0200792 // EM: disabled
793// ShibbolethUser user = User.UserFactory.getShibInstance((String) safeMap.get(Attributes.EPPN),
794// (String) safeMap.get(Attributes.MAIL), (String) safeMap.get(Attributes.CN));
795// user.setAffiliation((String) safeMap.get(Attributes.EDU_AFFIL));
796// user.setAccountCreation(TimeUtils.getNow().getMillis());
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200797
margarethafb027f92018-04-23 20:00:13 +0200798 ShibbolethUser user = null;
799
margarethaebc54962017-05-29 13:23:07 +0200800 UserDetails d = new UserDetails();
801 d.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200802
margarethaebc54962017-05-29 13:23:07 +0200803 UserSettings s = new UserSettings();
804 s.read(attributes, true);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200805
margarethaebc54962017-05-29 13:23:07 +0200806 entHandler.createAccount(user);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200807
margarethaebc54962017-05-29 13:23:07 +0200808 s.setUserId(user.getId());
809 d.setUserId(user.getId());
Michael Hanl25aac542016-02-01 18:16:44 +0100810
margarethaebc54962017-05-29 13:23:07 +0200811 UserDataDbIface dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserDetails.class);
812 assert dao != null;
813 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100814
margarethaebc54962017-05-29 13:23:07 +0200815 dao = BeansFactory.getTypeFactory().getTypeInterfaceBean(userdatadaos, UserSettings.class);
816 assert dao != null;
817 dao.store(d);
Michael Hanl25aac542016-02-01 18:16:44 +0100818
margarethaebc54962017-05-29 13:23:07 +0200819 return user;
820 }
Michael Hanl25aac542016-02-01 18:16:44 +0100821
margarethaebc54962017-05-29 13:23:07 +0200822 /**
823 * link shibboleth and korap user account to one another.
824 *
825 * @param current
826 * currently logged in user
827 * @param for_name
828 * foreign user name the current account should be linked to
829 * @param transstrat
830 * transfer status of user data (details, settings, user queries)
831 * 0 = the currently logged in data should be kept 1 = the
832 * foreign account data should be kept
833 * @throws NotAuthorizedException
834 * @throws KustvaktException
835 */
836 // todo:
837 public void accountLink(User current, String for_name, int transstrat) throws KustvaktException {
838 // User foreign = entHandler.getAccount(for_name);
Michael Hanl87106d12015-09-14 18:13:51 +0200839
margarethaebc54962017-05-29 13:23:07 +0200840 // if (current.getAccountLink() == null && current.getAccountLink()
841 // .isEmpty()) {
842 // if (current instanceof KorAPUser && foreign instanceof ShibUser) {
843 // if (transstrat == 1)
844 // current.transfer(foreign);
845 //// foreign.setAccountLink(current.getUsername());
846 //// current.setAccountLink(foreign.getUsername());
847 // // entHandler.purgeDetails(foreign);
848 // // entHandler.purgeSettings(foreign);
849 // }else if (foreign instanceof KorAPUser
850 // && current instanceof ShibUser) {
851 // if (transstrat == 0)
852 // foreign.transfer(current);
853 //// current.setAccountLink(foreign.getUsername());
854 // // entHandler.purgeDetails(current);
855 // // entHandler.purgeSettings(current);
856 // // entHandler.purgeSettings(current);
857 // }
858 // entHandler.updateAccount(current);
859 // entHandler.updateAccount(foreign);
860 // }
861 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200862
margarethaebc54962017-05-29 13:23:07 +0200863 // todo: test and rest usage?!
864 public boolean updateAccount(User user) throws KustvaktException {
865 boolean result;
866 if (user instanceof DemoUser)
867 throw new KustvaktException(user.getId(), StatusCodes.REQUEST_INVALID,
868 "account not updateable for demo user", user.getUsername());
869 else {
870 // crypto.validate(user);
871 try {
872 result = entHandler.updateAccount(user) > 0;
873 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200874 jlog.error("Error: "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200875 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
876 }
877 }
878 if (result) {
879 // this.removeCacheEntry(user.getUsername());
880 auditing.audit(
881 AuditRecord.serviceRecord(user.getId(), StatusCodes.UPDATE_ACCOUNT_SUCCESSFUL, user.toString()));
882 }
883 return result;
884 }
Michael Hanl87106d12015-09-14 18:13:51 +0200885
margarethaebc54962017-05-29 13:23:07 +0200886 public boolean deleteAccount(User user) throws KustvaktException {
887 boolean result;
888 if (user instanceof DemoUser)
889 return true;
890 else {
891 try {
892 result = entHandler.deleteAccount(user.getId()) > 0;
893 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200894 jlog.error("Error: "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200895 throw new WrappedException(e, StatusCodes.DELETE_ACCOUNT_FAILED);
896 }
897 }
898 if (result) {
899 // this.removeCacheEntry(user.getUsername());
900 auditing.audit(AuditRecord.serviceRecord(user.getUsername(), StatusCodes.DELETE_ACCOUNT_SUCCESSFUL,
901 user.toString()));
902 }
903 return result;
904 }
Michael Hanl87106d12015-09-14 18:13:51 +0200905
margarethaebc54962017-05-29 13:23:07 +0200906 public Object[] validateResetPasswordRequest(String username, String email) throws KustvaktException {
907 String uritoken;
908 validator.validateEntry(email, Attributes.EMAIL);
909 User ident;
910 try {
911 ident = entHandler.getAccount(username);
912 if (ident instanceof DemoUser)
913 // throw new
914 // NotAuthorizedException(StatusCodes.PERMISSION_DENIED,
915 // "password reset now allowed for DemoUser", "");
916 throw new WrappedException(username, StatusCodes.PASSWORD_RESET_FAILED, username);
917 } catch (EmptyResultException e) {
918 throw new WrappedException(
919 new KustvaktException(username, StatusCodes.ILLEGAL_ARGUMENT, "username not found", username),
920 StatusCodes.PASSWORD_RESET_FAILED, username);
921 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200922
margarethaebc54962017-05-29 13:23:07 +0200923 Userdata data = this.getUserData(ident, UserDetails.class);
924 KorAPUser user = (KorAPUser) ident;
Michael Hanl87106d12015-09-14 18:13:51 +0200925
margarethaebc54962017-05-29 13:23:07 +0200926 if (!email.equals(data.get(Attributes.EMAIL)))
927 // throw new NotAuthorizedException(StatusCodes.ILLEGAL_ARGUMENT,
928 // "invalid parameter: email", "email");
929 throw new WrappedException(
930 new KustvaktException(user.getId(), StatusCodes.ILLEGAL_ARGUMENT, "email invalid", email),
931 StatusCodes.PASSWORD_RESET_FAILED, email);
932 uritoken = crypto.encodeBase();
933 URIParam param = new URIParam(uritoken, TimeUtils.plusHours(24).getMillis());
934 user.addField(param);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200935
margarethaebc54962017-05-29 13:23:07 +0200936 try {
937 entHandler.updateAccount(user);
938 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200939 jlog.error("Error "+ e.string());
margarethaebc54962017-05-29 13:23:07 +0200940 throw new WrappedException(e, StatusCodes.PASSWORD_RESET_FAILED);
941 }
942 return new Object[] { uritoken, TimeUtils.format(param.getUriExpiration()) };
943 }
Michael Hanl87106d12015-09-14 18:13:51 +0200944
margaretha38d530e2017-07-11 19:06:50 +0200945 // EM: not in the new DB
margarethaebc54962017-05-29 13:23:07 +0200946 @Override
947 public <T extends Userdata> T getUserData(User user, Class<T> clazz) throws WrappedException {
margarethaebc54962017-05-29 13:23:07 +0200948 try {
949 UserDataDbIface<T> dao = BeansFactory.getTypeFactory()
950 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), clazz);
951 T data = null;
952 if (dao != null)
953 data = dao.get(user);
Michael Hanl87106d12015-09-14 18:13:51 +0200954
margarethaebc54962017-05-29 13:23:07 +0200955 if (data == null)
margarethaf6d5a822017-10-19 19:51:20 +0200956 throw new KustvaktException(user.getId(), StatusCodes.NO_RESULT_FOUND, "No data found!",
margarethaebc54962017-05-29 13:23:07 +0200957 clazz.getSimpleName());
958 return data;
959 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200960 jlog.error("Error during user data retrieval: "+ e.getEntity());
margarethaebc54962017-05-29 13:23:07 +0200961 throw new WrappedException(e, StatusCodes.GET_ACCOUNT_FAILED);
962 }
963 }
Michael Hanl5fac8ab2016-01-29 16:33:04 +0100964
margarethaebc54962017-05-29 13:23:07 +0200965 // todo: cache userdata outside of the user object!
966 @Override
967 public void updateUserData(Userdata data) throws WrappedException {
968 try {
969 data.validate(this.validator);
970 UserDataDbIface dao = BeansFactory.getTypeFactory()
971 .getTypeInterfaceBean(BeansFactory.getKustvaktContext().getUserDataProviders(), data.getClass());
972 if (dao != null)
973 dao.update(data);
974 } catch (KustvaktException e) {
margaretha49cb6882018-07-04 04:19:54 +0200975 jlog.error("Error during update of user data! "+ e.getEntity());
margarethaebc54962017-05-29 13:23:07 +0200976 throw new WrappedException(e, StatusCodes.UPDATE_ACCOUNT_FAILED);
977 }
978 }
margaretha2afb97d2017-12-07 19:18:44 +0100979
Michael Hanl87106d12015-09-14 18:13:51 +0200980}