[Fix] Allow user to retry password reset if failed password strength

This commit is contained in:
Robert von Burg 2023-03-14 09:18:15 +01:00
parent cace8c5156
commit 9334136c25
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
5 changed files with 46 additions and 34 deletions

View File

@ -0,0 +1,7 @@
package li.strolch.privilege.base;
public class PasswordStrengthException extends PrivilegeException {
public PasswordStrengthException(String string) {
super(string);
}
}

View File

@ -1882,9 +1882,9 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
} }
@Override @Override
public void validatePassword(Locale locale, char[] password) throws PrivilegeException { public void validatePassword(Locale locale, char[] password) throws PasswordStrengthException {
if (!this.passwordStrengthHandler.validateStrength(password)) if (!this.passwordStrengthHandler.validateStrength(password))
throw new PrivilegeException(this.passwordStrengthHandler.getDescription(locale)); throw new PasswordStrengthException(this.passwordStrengthHandler.getDescription(locale));
} }
@Override @Override

View File

@ -19,10 +19,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.*;
import li.strolch.privilege.base.NotAuthenticatedException;
import li.strolch.privilege.base.PrivilegeConflictResolution;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.*; import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User; import li.strolch.privilege.model.internal.User;
@ -40,8 +37,8 @@ public interface PrivilegeHandler {
/// ///
/** /**
* Privilege "PrivilegeAction" which is used for privileges which are not further categorized e.g. s {@link * Privilege "PrivilegeAction" which is used for privileges which are not further categorized e.g. s
* #PRIVILEGE_ACTION_PERSIST} and {@link #PRIVILEGE_ACTION_GET_POLICIES} * {@link #PRIVILEGE_ACTION_PERSIST} and {@link #PRIVILEGE_ACTION_GET_POLICIES}
*/ */
String PRIVILEGE_ACTION = "PrivilegeAction"; String PRIVILEGE_ACTION = "PrivilegeAction";
@ -360,7 +357,7 @@ public interface PrivilegeHandler {
* *
* <p> * <p>
* If the password given is null, then the user is created, but can not not login! Otherwise the password must meet * If the password given is null, then the user is created, but can not not login! Otherwise the password must meet
* the requirements of the implementation under {@link PrivilegeHandler#validatePassword(char[])} * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p> * </p>
* *
* @param certificate * @param certificate
@ -369,7 +366,7 @@ public interface PrivilegeHandler {
* the {@link UserRep} containing the information to create the new {@link User} * the {@link UserRep} containing the information to create the new {@link User}
* @param password * @param password
* the password of the new user. If the password is null, then this is accepted but the user can not login, * the password of the new user. If the password is null, then this is accepted but the user can not login,
* otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(char[])} * otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if the user for this certificate may not perform the action * if the user for this certificate may not perform the action
@ -386,8 +383,6 @@ public interface PrivilegeHandler {
* the {@link Certificate} of the user which has the privilege to perform this action * the {@link Certificate} of the user which has the privilege to perform this action
* @param userReps * @param userReps
* the list of users to add or update * the list of users to add or update
*
* @throws PrivilegeException
*/ */
void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException; void addOrUpdateUsers(Certificate certificate, List<UserRep> userReps) throws PrivilegeException;
@ -397,7 +392,7 @@ public interface PrivilegeHandler {
* will be updated on the existing user. The username on the given {@link UserRep} must be set and correspond to an * will be updated on the existing user. The username on the given {@link UserRep} must be set and correspond to an
* existing user. * existing user.
* </p> * </p>
* * <p>
* The following fields are considered updateable: * The following fields are considered updateable:
* <ul> * <ul>
* <li>{@link UserRep#getFirstname()}</li> * <li>{@link UserRep#getFirstname()}</li>
@ -429,7 +424,7 @@ public interface PrivilegeHandler {
* *
* <p> * <p>
* If the password given is null, then the user is created, but can not not login! Otherwise the password must meet * If the password given is null, then the user is created, but can not not login! Otherwise the password must meet
* the requirements of the implementation under {@link PrivilegeHandler#validatePassword(char[])} * the requirements of the implementation under {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p> * </p>
* *
* @param certificate * @param certificate
@ -438,7 +433,7 @@ public interface PrivilegeHandler {
* the {@link UserRep} containing the information to replace the existing {@link User} * the {@link UserRep} containing the information to replace the existing {@link User}
* @param password * @param password
* the password of the new user. If the password is null, then this is accepted but the user can not login, * the password of the new user. If the password is null, then this is accepted but the user can not login,
* otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(char[])} * otherwise the password must be validated against {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if the user for this certificate may not perform the action * if the user for this certificate may not perform the action
@ -515,8 +510,8 @@ public interface PrivilegeHandler {
/** /**
* <p> * <p>
* Changes the password for the {@link User} with the given username. If the password is null, then the {@link User} * Changes the password for the {@link User} with the given username. If the password is null, then the {@link User}
* can not login anymore. Otherwise the password must meet the requirements of the implementation under {@link * can not login anymore. Otherwise the password must meet the requirements of the implementation under
* PrivilegeHandler#validatePassword(char[])} * {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p> * </p>
* *
* <p> * <p>
@ -529,7 +524,8 @@ public interface PrivilegeHandler {
* the username of the {@link User} for which the password is to be changed * the username of the {@link User} for which the password is to be changed
* @param password * @param password
* the new password for this user. If the password is null, then the {@link User} can not login anymore. Otherwise * the new password for this user. If the password is null, then the {@link User} can not login anymore. Otherwise
* the password must meet the requirements of the implementation under {@link PrivilegeHandler#validatePassword(char[])} * the password must meet the requirements of the implementation under
* {@link PrivilegeHandler#validatePassword(Locale, char[])}
* *
* @throws AccessDeniedException * @throws AccessDeniedException
* if the user for this certificate may not perform the action * if the user for this certificate may not perform the action
@ -653,7 +649,7 @@ public interface PrivilegeHandler {
* the username of the {@link User} which is registered in the {@link PersistenceHandler} * the username of the {@link User} which is registered in the {@link PersistenceHandler}
* @param password * @param password
* the password with which this user is to be authenticated. Null passwords are not accepted and they must meet * the password with which this user is to be authenticated. Null passwords are not accepted and they must meet
* the requirements of the {@link #validatePassword(char[])}-method * the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param keepAlive * @param keepAlive
* should this session be kept alive * should this session be kept alive
* *
@ -672,7 +668,7 @@ public interface PrivilegeHandler {
* the username of the {@link User} which is registered in the {@link PersistenceHandler} * the username of the {@link User} which is registered in the {@link PersistenceHandler}
* @param password * @param password
* the password with which this user is to be authenticated. Null passwords are not accepted and they must meet * the password with which this user is to be authenticated. Null passwords are not accepted and they must meet
* the requirements of the {@link #validatePassword(char[])}-method * the requirements of the {@link #validatePassword(Locale, char[])}-method
* @param source * @param source
* the source of the authentication request, i.e. remote IP * the source of the authentication request, i.e. remote IP
* @param usage * @param usage
@ -763,7 +759,7 @@ public interface PrivilegeHandler {
/** /**
* Checks if the given {@link Certificate} is valid. This means that the certificate is for a valid session and that * Checks if the given {@link Certificate} is valid. This means that the certificate is for a valid session and that
* the user exists for the certificate. This method checks if the {@link Certificate} has been tampered with * the user exists for the certificate. This method checks if the {@link Certificate} has been tampered with
* * <p>
* Returns the {@link PrivilegeContext} for the given {@link Certificate}. The {@link PrivilegeContext} is an * Returns the {@link PrivilegeContext} for the given {@link Certificate}. The {@link PrivilegeContext} is an
* encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their * encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their
* actions and do not need to access the {@link PrivilegeHandler} anymore * actions and do not need to access the {@link PrivilegeHandler} anymore
@ -798,7 +794,7 @@ public interface PrivilegeHandler {
/** /**
* Checks if the given {@link Certificate} is valid. This means that the certificate is for a valid session and that * Checks if the given {@link Certificate} is valid. This means that the certificate is for a valid session and that
* the user exists for the certificate. This method checks if the {@link Certificate} has been tampered with * the user exists for the certificate. This method checks if the {@link Certificate} has been tampered with
* * <p>
* Returns the {@link PrivilegeContext} for the given {@link Certificate}. The {@link PrivilegeContext} is an * Returns the {@link PrivilegeContext} for the given {@link Certificate}. The {@link PrivilegeContext} is an
* encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their * encapsulated state of a user's privileges so that for the duration of a user's call, the user can perform their
* actions and do not need to access the {@link PrivilegeHandler} anymore * actions and do not need to access the {@link PrivilegeHandler} anymore
@ -820,7 +816,7 @@ public interface PrivilegeHandler {
/** /**
* @see li.strolch.privilege.handler.PasswordStrengthHandler#validateStrength(char[]) * @see li.strolch.privilege.handler.PasswordStrengthHandler#validateStrength(char[])
*/ */
void validatePassword(Locale locale, char[] password) throws PrivilegeException; void validatePassword(Locale locale, char[] password) throws PasswordStrengthException;
/** /**
* <p> * <p>
@ -908,9 +904,9 @@ public interface PrivilegeHandler {
/** /**
* Special method to open a {@link PrivilegeContext} as a System user, meaning the given systemUsername corresponds * Special method to open a {@link PrivilegeContext} as a System user, meaning the given systemUsername corresponds
* to an account which has the state {@link UserState#SYSTEM}. This is used in cases where a system user's {@link * to an account which has the state {@link UserState#SYSTEM}. This is used in cases where a system user's
* PrivilegeContext} should be open for a longer period of time, or where opening many {@link PrivilegeContext} is * {@link PrivilegeContext} should be open for a longer period of time, or where opening many
* resource intensive e.g. on low power devices. * {@link PrivilegeContext} is resource intensive e.g. on low power devices.
* *
* @param systemUsername * @param systemUsername
* the username of the system user to perform the action as * the username of the system user to perform the action as

View File

@ -15,17 +15,13 @@
*/ */
package li.strolch.rest.endpoint; package li.strolch.rest.endpoint;
import static jakarta.ws.rs.core.Response.Status.NOT_ACCEPTABLE;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
import static li.strolch.privilege.handler.PrivilegeHandler.PRIVILEGE_GET_USER; import static li.strolch.privilege.handler.PrivilegeHandler.PRIVILEGE_GET_USER;
import static li.strolch.rest.helper.ResponseUtil.toResponse; import static li.strolch.rest.helper.ResponseUtil.toResponse;
import static li.strolch.rest.helper.RestfulHelper.toJson; import static li.strolch.rest.helper.RestfulHelper.toJson;
import static li.strolch.search.SearchBuilder.buildSimpleValueSearch; import static li.strolch.search.SearchBuilder.buildSimpleValueSearch;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
@ -33,11 +29,17 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import com.google.gson.*; import com.google.gson.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Tags; import li.strolch.model.Tags;
import li.strolch.model.json.PrivilegeElementFromJsonVisitor; import li.strolch.model.json.PrivilegeElementFromJsonVisitor;
import li.strolch.model.json.PrivilegeElementToJsonVisitor; import li.strolch.model.json.PrivilegeElementToJsonVisitor;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.base.PasswordStrengthException;
import li.strolch.privilege.handler.PrivilegeHandler; import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.UserRep; import li.strolch.privilege.model.UserRep;
@ -312,8 +314,11 @@ public class PrivilegeUsersService {
arg.password = passwordString.toCharArray(); arg.password = passwordString.toCharArray();
ServiceResult svcResult = svcHandler.doService(cert, svc, arg); ServiceResult svcResult = svcHandler.doService(cert, svc, arg);
if (svcResult.isNok()) if (svcResult.isNok()) {
if (svcResult.getRootCause() instanceof PasswordStrengthException)
return toResponse(NOT_ACCEPTABLE, svcResult.getRootCause());
return toResponse(svcResult); return toResponse(svcResult);
}
// if user changes their own password, then invalidate the session // if user changes their own password, then invalidate the session
if (cert.getUsername().equals(username)) { if (cert.getUsername().equals(username)) {

View File

@ -20,6 +20,7 @@ import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter; import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider; import jakarta.ws.rs.ext.Provider;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
@ -47,8 +48,11 @@ public class AuthenticationResponseFilter implements ContainerResponseFilter {
logger.info("Invalidating single usage certificate for " + cert.getUsername()); logger.info("Invalidating single usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert); RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
} else if (cert.getUsage().isSetPassword()) { } else if (cert.getUsage().isSetPassword()) {
logger.info("Invalidating SET_PASSWORD usage certificate for " + cert.getUsername()); // if not acceptable, then user can try again
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert); if (responseContext.getStatusInfo().toEnum() != Response.Status.NOT_ACCEPTABLE) {
logger.info("Invalidating SET_PASSWORD usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
}
} }
} }
} }