[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
public void validatePassword(Locale locale, char[] password) throws PrivilegeException {
public void validatePassword(Locale locale, char[] password) throws PasswordStrengthException {
if (!this.passwordStrengthHandler.validateStrength(password))
throw new PrivilegeException(this.passwordStrengthHandler.getDescription(locale));
throw new PasswordStrengthException(this.passwordStrengthHandler.getDescription(locale));
}
@Override

View File

@ -19,10 +19,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import li.strolch.privilege.base.AccessDeniedException;
import li.strolch.privilege.base.NotAuthenticatedException;
import li.strolch.privilege.base.PrivilegeConflictResolution;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.base.*;
import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.Role;
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_ACTION_PERSIST} and {@link #PRIVILEGE_ACTION_GET_POLICIES}
* Privilege "PrivilegeAction" which is used for privileges which are not further categorized e.g. s
* {@link #PRIVILEGE_ACTION_PERSIST} and {@link #PRIVILEGE_ACTION_GET_POLICIES}
*/
String PRIVILEGE_ACTION = "PrivilegeAction";
@ -360,7 +357,7 @@ public interface PrivilegeHandler {
*
* <p>
* 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>
*
* @param certificate
@ -369,7 +366,7 @@ public interface PrivilegeHandler {
* the {@link UserRep} containing the information to create the new {@link User}
* @param password
* 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
* 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
* @param userReps
* the list of users to add or update
*
* @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
* existing user.
* </p>
*
* <p>
* The following fields are considered updateable:
* <ul>
* <li>{@link UserRep#getFirstname()}</li>
@ -429,7 +424,7 @@ public interface PrivilegeHandler {
*
* <p>
* 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>
*
* @param certificate
@ -438,7 +433,7 @@ public interface PrivilegeHandler {
* the {@link UserRep} containing the information to replace the existing {@link User}
* @param password
* 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
* if the user for this certificate may not perform the action
@ -515,8 +510,8 @@ public interface PrivilegeHandler {
/**
* <p>
* 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
* PrivilegeHandler#validatePassword(char[])}
* can not login anymore. Otherwise the password must meet the requirements of the implementation under
* {@link PrivilegeHandler#validatePassword(Locale, char[])}
* </p>
*
* <p>
@ -529,7 +524,8 @@ public interface PrivilegeHandler {
* the username of the {@link User} for which the password is to be changed
* @param password
* 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
* 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}
* @param password
* 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
* 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}
* @param password
* 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
* the source of the authentication request, i.e. remote IP
* @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
* 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
* 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
@ -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
* 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
* 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
@ -820,7 +816,7 @@ public interface PrivilegeHandler {
/**
* @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>
@ -908,9 +904,9 @@ public interface PrivilegeHandler {
/**
* 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
* PrivilegeContext} should be open for a longer period of time, or where opening many {@link PrivilegeContext} is
* resource intensive e.g. on low power devices.
* to an account which has the state {@link UserState#SYSTEM}. This is used in cases where a system user's
* {@link PrivilegeContext} should be open for a longer period of time, or where opening many
* {@link PrivilegeContext} is resource intensive e.g. on low power devices.
*
* @param systemUsername
* the username of the system user to perform the action as

View File

@ -15,17 +15,13 @@
*/
package li.strolch.rest.endpoint;
import static jakarta.ws.rs.core.Response.Status.NOT_ACCEPTABLE;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.handler.PrivilegeHandler.PRIVILEGE_GET_USER;
import static li.strolch.rest.helper.ResponseUtil.toResponse;
import static li.strolch.rest.helper.RestfulHelper.toJson;
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.util.Arrays;
import java.util.Base64;
@ -33,11 +29,17 @@ import java.util.List;
import java.util.Locale;
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.model.Tags;
import li.strolch.model.json.PrivilegeElementFromJsonVisitor;
import li.strolch.model.json.PrivilegeElementToJsonVisitor;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.base.PasswordStrengthException;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.UserRep;
@ -312,8 +314,11 @@ public class PrivilegeUsersService {
arg.password = passwordString.toCharArray();
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);
}
// if user changes their own password, then invalidate the session
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.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import li.strolch.privilege.model.Certificate;
@ -47,8 +48,11 @@ public class AuthenticationResponseFilter implements ContainerResponseFilter {
logger.info("Invalidating single usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
} else if (cert.getUsage().isSetPassword()) {
logger.info("Invalidating SET_PASSWORD usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
// if not acceptable, then user can try again
if (responseContext.getStatusInfo().toEnum() != Response.Status.NOT_ACCEPTABLE) {
logger.info("Invalidating SET_PASSWORD usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
}
}
}
}