Skip to content

Commit

Permalink
Permet la désactivation à la place du retrait d’un groupe (#1030)
Browse files Browse the repository at this point in the history
  • Loading branch information
niladic authored May 11, 2021
1 parent 1e7242f commit 47a4b57
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 193 deletions.
6 changes: 6 additions & 0 deletions app/constants/Constants.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ package constants

object Constants {
val supportEmail = "support@aplus.beta.gouv.fr"

val error500FlashMessage =
"Une erreur interne est survenue. " +
"Celle-ci étant possiblement temporaire, " +
"nous vous invitons à réessayer plus tard."

}
44 changes: 27 additions & 17 deletions app/controllers/Operators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import java.util.UUID

import actions.RequestWithUserData
import cats.syntax.all._
import constants.Constants
import helper.BooleanHelper.not
import models.EventType._
Expand Down Expand Up @@ -60,33 +61,42 @@ object Operators {

import Results._

def withUser(userId: UUID, includeDisabled: Boolean = false)(
def withUser(
userId: UUID,
includeDisabled: Boolean = false,
errorMessage: String = "Tentative d'accès à un utilisateur inexistant",
errorResult: Option[Result] = none
)(
payload: User => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_]): Future[Result] =
userService
.byId(userId, includeDisabled)
.fold({
eventService
.log(UserNotFound, description = "Tentative d'accès à un utilisateur inexistant.")
Future(NotFound("Utilisateur inexistant."))
eventService.log(UserNotFound, description = errorMessage)
Future.successful(
errorResult.getOrElse(NotFound("Utilisateur inexistant"))
)
})({ user: User => payload(user) })

def asUserWithAuthorization(authorizationCheck: Authorization.Check)(
event: () => (EventType, String)
errorEvent: () => (EventType, String),
errorResult: Option[Result] = none
)(
payload: () => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
if (not(authorizationCheck(request.rights))) {
val (eventType, description) = event()
eventService.log(eventType, description = description)
Future(Unauthorized("Vous n'avez pas le droit de faire ça"))
} else {
)(implicit request: RequestWithUserData[_]): Future[Result] =
if (authorizationCheck(request.rights)) {
payload()
} else {
val (eventType, description) = errorEvent()
eventService.log(eventType, description = description)
Future.successful(
errorResult.getOrElse(Unauthorized("Vous n'avez pas le droit de faire ça"))
)
}

def asAdmin(event: () => (EventType, String))(
payload: () => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_], ec: ExecutionContext): Future[Result] =
if (not(request.currentUser.admin)) {
val (eventType, description) = event()
eventService.log(eventType, description = description)
Expand All @@ -97,7 +107,7 @@ object Operators {

def asAdminWhoSeesUsersOfArea(areaId: UUID)(event: () => (EventType, String))(
payload: () => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_], ec: ExecutionContext): Future[Result] =
if (not(request.currentUser.admin) || not(request.currentUser.canSeeUsersInArea(areaId))) {
val (eventType, description) = event()
eventService.log(eventType, description = description)
Expand All @@ -108,7 +118,7 @@ object Operators {

def asUserWhoSeesUsersOfArea(areaId: UUID)(event: () => (EventType, String))(
payload: () => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_], ec: ExecutionContext): Future[Result] =
// TODO: use only Authorization
if (
not(
Expand All @@ -125,7 +135,7 @@ object Operators {

def asAdminOfUserZone(user: User)(event: () => (EventType, String))(
payload: () => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_], ec: ExecutionContext): Future[Result] =
if (not(request.currentUser.admin)) {
val (eventType, description) = event()
eventService.log(eventType, description = description)
Expand Down Expand Up @@ -176,7 +186,7 @@ object Operators {
applicationId: UUID
)(
payload: Application => Future[Result]
)(implicit request: RequestWithUserData[AnyContent], ec: ExecutionContext): Future[Result] =
)(implicit request: RequestWithUserData[_], ec: ExecutionContext): Future[Result] =
applicationService
.byId(
applicationId,
Expand Down
5 changes: 1 addition & 4 deletions app/controllers/SignupController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,9 @@ case class SignupController @Inject() (
_.fold(
e => {
eventService.logErrorNoUser(e)
val message = "Une erreur interne est survenue. " +
"Celle-ci étant possiblement temporaire, " +
"nous vous invitons à réessayer plus tard."
Future.successful(
Redirect(routes.LoginController.login)
.flashing("error" -> message)
.flashing("error" -> Constants.error500FlashMessage)
.withSession(request.session - Keys.Session.signupId)
)
},
Expand Down
212 changes: 144 additions & 68 deletions app/controllers/UserController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.util.UUID

import actions.{LoginAction, RequestWithUserData}
import cats.syntax.all._
import constants.Constants
import controllers.Operators.{GroupOperators, UserOperators}
import helper.BooleanHelper.not
import helper.StringHelper.{capitalizeName, commonStringInputNormalization}
Expand Down Expand Up @@ -90,21 +91,28 @@ case class UserController @Inject() (

private def getGroupsUsersAndApplicationsBy(user: User) = for {
groups <- groupService.byIdsFuture(user.groupIds)
users <- userService.byGroupIdsFuture(groups.map(_.id))
users <- userService.byGroupIdsFuture(groups.map(_.id), includeDisabled = true)
applications <- applicationService.allForUserIds(users.map(_.id))
} yield (groups, users, applications)

private def updateMyGroupsNotAllowed(groupId: UUID)(implicit request: RequestWithUserData[_]) = {
eventService.log(
EditMyGroupUpdatedError,
s"L'utilisateur n'est pas autorisé à éditer le groupe $groupId"
)
val message = "Vous n’avez pas le droit de modifier ce groupe"
Redirect(routes.UserController.showEditMyGroups).flashing("error" -> message)
}
private def myGroupsAction(
groupId: UUID
)(inner: RequestWithUserData[_] => Future[Result]): Action[AnyContent] =
loginAction.async { implicit request =>
asUserWithAuthorization(Authorization.canAddOrRemoveOtherUser(groupId))(
() =>
(
EventType.EditMyGroupUnauthorized,
s"L'utilisateur n'est pas autorisé à éditer le groupe $groupId"
), {
val message = "Vous n’avez pas le droit de modifier ce groupe"
Redirect(routes.UserController.showEditMyGroups).flashing("error" -> message).some
}
)(() => inner(request))
}

def addToGroup(groupId: UUID) =
loginAction.async { implicit request =>
myGroupsAction(groupId) { implicit request =>
val user = request.currentUser
AddUserToGroupFormData.form
.bindFromRequest()
Expand All @@ -117,71 +125,139 @@ case class UserController @Inject() (
}
},
data =>
if (user.belongsTo(groupId)) {
userService
.byEmailFuture(data.email)
.zip(userService.byGroupIdsFuture(List(groupId), includeDisabled = true))
.flatMap {
case (None, _) =>
eventService.log(
EditMyGroupBadUserInput,
s"Tentative d'ajout de l'utilisateur inexistant ${data.email} au groupe $groupId"
)
successful(
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> "L’utilisateur n’existe pas dans Administration+")
)
case (Some(userToAdd), usersInGroup)
if usersInGroup.map(_.id).contains[UUID](userToAdd.id) =>
eventService.log(
EditMyGroupBadUserInput,
s"Tentative d'ajout de l'utilisateur ${userToAdd.id} déjà présent au groupe $groupId",
involvesUser = userToAdd.id.some
)
successful(
userService
.byEmailFuture(data.email)
.zip(userService.byGroupIdsFuture(List(groupId), includeDisabled = true))
.flatMap {
case (None, _) =>
eventService.log(
EditMyGroupBadUserInput,
s"Tentative d'ajout de l'utilisateur inexistant ${data.email} au groupe $groupId"
)
successful(
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> "L’utilisateur n’existe pas dans Administration+")
)
case (Some(userToAdd), usersInGroup)
if usersInGroup.map(_.id).contains[UUID](userToAdd.id) =>
eventService.log(
EditMyGroupBadUserInput,
s"Tentative d'ajout de l'utilisateur ${userToAdd.id} déjà présent au groupe $groupId",
involvesUser = userToAdd.id.some
)
successful(
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> "L’utilisateur est déjà présent dans le groupe")
)
case (Some(userToAdd), _) =>
userService
.addToGroup(userToAdd.id, groupId)
.map { _ =>
eventService.log(
EditMyGroupUpdated,
s"Utilisateur ${userToAdd.id} ajouté au groupe $groupId",
involvesUser = userToAdd.id.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> "L’utilisateur est déjà présent dans le groupe")
)
case (Some(userToAdd), _) =>
userService
.addToGroup(userToAdd.id, groupId)
.map { _ =>
eventService.log(
EditMyGroupUpdated,
s"Utilisateur ${userToAdd.id} ajouté au groupe $groupId",
involvesUser = userToAdd.id.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("success" -> "L’utilisateur a été ajouté au groupe")
}
}
} else successful(updateMyGroupsNotAllowed(groupId))
.flashing("success" -> "L’utilisateur a été ajouté au groupe")
}
}
)
}

def removeFromGroup(userId: UUID, groupId: UUID) =
def enableUser(userId: UUID) =
loginAction.async { implicit request =>
if (request.currentUser.belongsTo(groupId))
userService
.removeFromGroup(userId, groupId)
.map { _ =>
eventService.log(
EditMyGroupUpdated,
s"Utilisateur $userId retiré du groupe $groupId",
involvesUser = userId.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("success" -> "L’utilisateur a bien été retiré du groupe.")
}
.recover { e =>
eventService.log(
EditMyGroupUpdatedError,
s"Erreur lors de la tentative d'ajout de l'utilisateur $userId au groupe $groupId : ${e.getMessage}"
withUser(
userId,
includeDisabled = true,
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être réactivé",
errorResult = Redirect(routes.UserController.showEditMyGroups)
.flashing(
"error" -> ("L’utilisateur n’existe pas dans Administration+. " +
"S’il s’agit d’une erreur, vous pouvez contacter le support.")
)
.some
) { otherUser =>
asUserWithAuthorization(Authorization.canEnableOtherUser(otherUser))(() =>
(
EventType.EditUserUnauthorized,
s"L'utilisateur n'est pas autorisé à réactiver l'utilisateur $userId"
)
) { () =>
userService
.enable(userId)
.map(
_.fold(
error => {
eventService.logError(error)
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> Constants.error500FlashMessage)
},
_ => {
eventService.log(
EventType.UserEdited,
s"Utilisateur $userId réactivé",
involvesUser = userId.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("success" -> "L’utilisateur a bien été réactivé.")
}
)
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> "Une erreur technique est survenue")
}
}
}

def removeFromGroup(userId: UUID, groupId: UUID) =
myGroupsAction(groupId) { implicit request =>
withUser(
userId,
includeDisabled = true,
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être désactivé",
errorResult = Redirect(routes.UserController.showEditMyGroups)
.flashing(
"error" -> ("L’utilisateur n’existe pas dans Administration+. " +
"S’il s’agit d’une erreur, vous pouvez contacter le support.")
)
.some
) { otherUser =>
val result: Future[Either[Error, Result]] =
if (otherUser.groupIds.toSet.size <= 1) {
userService
.disable(userId)
.map(_.map { _ =>
eventService.log(
EventType.UserEdited,
s"Utilisateur $userId désactivé",
involvesUser = userId.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("success" -> "L’utilisateur a bien été désactivé.")
})
} else {
userService
.removeFromGroup(userId, groupId)
.map(_.map { _ =>
eventService.log(
EditMyGroupUpdated,
s"Utilisateur $userId retiré du groupe $groupId",
involvesUser = userId.some
)
Redirect(routes.UserController.showEditMyGroups)
.flashing("success" -> "L’utilisateur a bien été retiré du groupe.")
})
}
else successful(updateMyGroupsNotAllowed(groupId))
result.map(
_.fold(
error => {
eventService.logError(error)
Redirect(routes.UserController.showEditMyGroups)
.flashing("error" -> Constants.error500FlashMessage)
},
identity
)
)
}
}

def showEditMyGroups =
Expand Down
Loading

0 comments on commit 47a4b57

Please sign in to comment.