mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
add permission to modify the own password over the me and the user endpoints
This commit is contained in:
@@ -63,6 +63,6 @@ public class IllegalArgumentExceptionMapper
|
||||
public Response toResponse(IllegalArgumentException exception)
|
||||
{
|
||||
log.info("caught IllegalArgumentException -- mapping to bad request", exception);
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.github.sdorra.ssp.PermissionCheck;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.slf4j.Logger;
|
||||
@@ -63,6 +64,8 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
@@ -196,27 +199,24 @@ public abstract class AbstractManagerResource<T extends ModelObject> {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
* @param item
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Response update(String name, T item)
|
||||
{
|
||||
public Response update(T item, Function<T, PermissionCheck> permissionChecker ){
|
||||
Consumer<Manager> updateAction = mng -> mng.modify(item, permissionChecker);
|
||||
return applyUpdate(item, updateAction);
|
||||
}
|
||||
|
||||
public Response update(String name, T item) {
|
||||
Consumer<Manager> updateAction = mng -> mng.modify(item);
|
||||
return applyUpdate(item, updateAction);
|
||||
}
|
||||
|
||||
public Response applyUpdate(T item, Consumer<Manager> updateAction) {
|
||||
Response response = null;
|
||||
|
||||
preUpdate(item);
|
||||
|
||||
try
|
||||
{
|
||||
manager.modify(item);
|
||||
updateAction.accept(manager);
|
||||
response = Response.noContent().build();
|
||||
}
|
||||
catch (AuthorizationException ex)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.ssp.PermissionCheck;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import sonia.scm.AlreadyExistsException;
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
@@ -7,6 +8,10 @@ import sonia.scm.Manager;
|
||||
import sonia.scm.ModelObject;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.PageResult;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserPermissions;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
import sonia.scm.util.AuthenticationUtil;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Optional;
|
||||
@@ -38,13 +43,31 @@ class IdResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
return singleAdapter.get(loadBy(id), mapToDto);
|
||||
}
|
||||
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Consumer<MODEL_OBJECT> checker) throws NotFoundException, ConcurrentModificationException {
|
||||
return singleAdapter.update(
|
||||
|
||||
/**
|
||||
* If the authenticated user is the same user that want to change password than return the changeOwnPassword verification function
|
||||
* if the authenticated user is different he should have the modify permission to be able to modify passwords of other users
|
||||
*
|
||||
* @param usernameToChangePassword the user name of the user we want to change password
|
||||
* @return function to verify permission
|
||||
*/
|
||||
public Function<User, PermissionCheck> getChangePasswordPermission(String usernameToChangePassword) {
|
||||
AssertUtil.assertIsNotEmpty(usernameToChangePassword);
|
||||
return user -> {
|
||||
if (usernameToChangePassword.equals(AuthenticationUtil.getAuthenticatedUsername())) {
|
||||
return UserPermissions.changeOwnPassword();
|
||||
}
|
||||
return UserPermissions.modify(user);
|
||||
};
|
||||
}
|
||||
|
||||
public Response changePassword(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Consumer<MODEL_OBJECT> checker, Function<MODEL_OBJECT, PermissionCheck> permissionCheck) throws NotFoundException, ConcurrentModificationException {
|
||||
return singleAdapter.changePassword(
|
||||
loadBy(id),
|
||||
applyChanges,
|
||||
idStaysTheSame(id),
|
||||
checker
|
||||
);
|
||||
checker,
|
||||
permissionCheck);
|
||||
}
|
||||
|
||||
public Response update(String id, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges) throws NotFoundException, ConcurrentModificationException {
|
||||
|
||||
@@ -10,6 +10,7 @@ import sonia.scm.NotFoundException;
|
||||
import sonia.scm.user.InvalidPasswordException;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.user.UserPermissions;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -80,7 +81,7 @@ public class MeResource {
|
||||
@Consumes(VndMediaType.PASSWORD_CHANGE)
|
||||
public Response changePassword(PasswordChangeDto passwordChangeDto) throws NotFoundException, ConcurrentModificationException {
|
||||
String name = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
|
||||
return adapter.update(name, user -> user.changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getUserTypeChecker().andThen(getOldOriginalPasswordChecker(passwordChangeDto.getOldPassword())));
|
||||
return adapter.changePassword(name, user -> user.clone().changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getChangePasswordChecker().andThen(getOldOriginalPasswordChecker(passwordChangeDto.getOldPassword())), user -> UserPermissions.changeOwnPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.ssp.PermissionCheck;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
import sonia.scm.Manager;
|
||||
@@ -16,8 +17,6 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* Adapter from resource http endpoints to managers, for Single resources (e.g. {@code /user/name}).
|
||||
*
|
||||
@@ -54,10 +53,12 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
.map(Response.ResponseBuilder::build)
|
||||
.orElseThrow(NotFoundException::new);
|
||||
}
|
||||
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey, Consumer<MODEL_OBJECT> checker) throws NotFoundException, ConcurrentModificationException {
|
||||
public Response changePassword(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey, Consumer<MODEL_OBJECT> checker, Function<MODEL_OBJECT, PermissionCheck> permissionCheck) throws NotFoundException, ConcurrentModificationException {
|
||||
MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new);
|
||||
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject);
|
||||
checkForUpdate(hasSameKey, existingModelObject, changedModelObject);
|
||||
checker.accept(existingModelObject);
|
||||
return update(reader,applyChanges,hasSameKey);
|
||||
return update(changedModelObject, permissionCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,13 +68,17 @@ class SingleResourceManagerAdapter<MODEL_OBJECT extends ModelObject,
|
||||
public Response update(Supplier<Optional<MODEL_OBJECT>> reader, Function<MODEL_OBJECT, MODEL_OBJECT> applyChanges, Predicate<MODEL_OBJECT> hasSameKey) throws NotFoundException, ConcurrentModificationException {
|
||||
MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new);
|
||||
MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject);
|
||||
checkForUpdate(hasSameKey, existingModelObject, changedModelObject);
|
||||
return update(getId(existingModelObject), changedModelObject);
|
||||
}
|
||||
|
||||
public void checkForUpdate(Predicate<MODEL_OBJECT> hasSameKey, MODEL_OBJECT existingModelObject, MODEL_OBJECT changedModelObject) throws ConcurrentModificationException {
|
||||
if (!hasSameKey.test(changedModelObject)) {
|
||||
return Response.status(BAD_REQUEST).entity("illegal change of id").build();
|
||||
throw new IllegalArgumentException("illegal change of id");
|
||||
}
|
||||
else if (modelObjectWasModifiedConcurrently(existingModelObject, changedModelObject)) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
return update(getId(existingModelObject), changedModelObject);
|
||||
}
|
||||
|
||||
private boolean modelObjectWasModifiedConcurrently(MODEL_OBJECT existing, MODEL_OBJECT updated) {
|
||||
|
||||
@@ -111,7 +111,9 @@ public class UserResource {
|
||||
* The oldPassword property of the DTO is not needed here. it will be ignored.
|
||||
* The oldPassword property is needed in the MeResources when the actual user change the own password.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "user:modify" privilege.
|
||||
* <strong>Note:</strong> This method requires "user:modify" privilege to modify the password of other users.
|
||||
* <strong>Note:</strong> This method requires "user:changeOwnPassword" privilege to modify the own password.
|
||||
*
|
||||
* @param name name of the user to be modified
|
||||
* @param passwordChangeDto change password object to modify password. the old password is here not required
|
||||
*/
|
||||
@@ -128,7 +130,7 @@ public class UserResource {
|
||||
})
|
||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
||||
public Response changePassword(@PathParam("id") String name, @Valid PasswordChangeDto passwordChangeDto) throws NotFoundException, ConcurrentModificationException {
|
||||
return adapter.update(name, user -> user.changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getUserTypeChecker());
|
||||
return adapter.changePassword(name, user -> user.changePassword(passwordService.encryptPassword(passwordChangeDto.getNewPassword())), userManager.getChangePasswordChecker(), adapter.getChangePasswordPermission(name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user