Skip to content

Commit

Permalink
Ajoute une vue groupes et stats d’un utilisateur pour les admins (#1203)
Browse files Browse the repository at this point in the history
  • Loading branch information
niladic authored Nov 3, 2021
1 parent 50dfe59 commit 6e33f12
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 118 deletions.
167 changes: 82 additions & 85 deletions app/controllers/ApplicationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ case class ApplicationController @Inject() (
extends InjectedController
with play.api.i18n.I18nSupport
with Operators.Common
with Operators.ApplicationOperators {
with Operators.ApplicationOperators
with Operators.UserOperators {

private val filesPath = configuration.underlying.getString("app.filesPath")
private val featureMandatSms: Boolean = configuration.get[Boolean]("app.features.smsMandat")
Expand Down Expand Up @@ -512,16 +513,14 @@ case class ApplicationController @Inject() (
applications <- applicationService.allForUserIds(users.map(_.id))
} yield (users, applications)

private def generateStats[A](
private def generateStats(
areaIds: List[UUID],
organisationIds: List[Organisation.Id],
groupIds: List[UUID],
creationMinDate: LocalDate,
creationMaxDate: LocalDate
)(implicit
webJarsUtil: org.webjars.play.WebJarsUtil,
request: RequestWithUserData[A]
): Future[Html] = {
creationMaxDate: LocalDate,
rights: Authorization.UserRights
)(implicit webJarsUtil: WebJarsUtil): Future[Html] = {
val usersAndApplications: Future[(List[User], List[Application])] =
(areaIds, organisationIds, groupIds) match {
case (Nil, Nil, Nil) =>
Expand Down Expand Up @@ -592,7 +591,7 @@ case class ApplicationController @Inject() (
users <- usersAndApplications.map { case (users, _) => users }
applications <- applicationsFuture
relatedUsers <- relatedUsersFuture
} yield views.html.stats.charts(Authorization.isAdmin(request.rights))(
} yield views.html.stats.charts(Authorization.isAdmin(rights))(
statsAggregates(applications, relatedUsers),
users
)
Expand All @@ -615,114 +614,112 @@ case class ApplicationController @Inject() (

def stats: Action[AnyContent] =
loginAction.async { implicit request =>
// TODO: remove `.get`
val (areaIds, organisationIds, groupIds, creationMinDate, creationMaxDate) =
statsForm.bindFromRequest().value.get
statsPage(routes.ApplicationController.stats, request.currentUser, request.rights)
}

val observableOrganisationIds = if (Authorization.isAdmin(request.rights)) {
organisationIds
} else {
organisationIds.filter(id => Authorization.canObserveOrganisation(id)(request.rights))
def statsAs(otherUserId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
withUser(otherUserId) { otherUser: User =>
asUserWithAuthorization(Authorization.canSeeOtherUserNonPrivateViews(otherUser))(
() =>
EventType.MasqueradeUnauthorized -> s"Accès non autorisé pour voir la page stats de $otherUserId",
errorInvolvesUser = Some(otherUser.id)
) { () =>
LoginAction.readUserRights(otherUser).flatMap { userRights =>
statsPage(routes.ApplicationController.statsAs(otherUserId), otherUser, userRights)
}
}
}
}

val observableGroupIds = if (Authorization.isAdmin(request.rights)) {
groupIds
} else {
groupIds.intersect(request.currentUser.groupIds)
}
private def statsPage(formUrl: Call, user: User, rights: Authorization.UserRights)(implicit
request: RequestWithUserData[_]
): Future[Result] = {
// TODO: remove `.get`
val (areaIds, organisationIds, groupIds, creationMinDate, creationMaxDate) =
statsForm.bindFromRequest().value.get

val cacheKey =
Authorization.isAdmin(request.rights).toString +
".stats." +
Hash.sha256(
areaIds.toString + observableOrganisationIds.toString + observableGroupIds.toString +
creationMinDate.toString + creationMaxDate.toString
)
val observableOrganisationIds = if (Authorization.isAdmin(rights)) {
organisationIds
} else {
organisationIds.filter(id => Authorization.canObserveOrganisation(id)(rights))
}

val observableGroupIds = if (Authorization.isAdmin(rights)) {
groupIds
} else {
groupIds.intersect(user.groupIds)
}

userGroupService.byIdsFuture(request.currentUser.groupIds).flatMap { currentUserGroups =>
cache
.getOrElseUpdate[Html](cacheKey, 1.hours)(
generateStats(
val cacheKey =
Authorization.isAdmin(rights).toString +
".stats." +
Hash.sha256(
areaIds.toString + observableOrganisationIds.toString + observableGroupIds.toString +
creationMinDate.toString + creationMaxDate.toString
)

userGroupService.byIdsFuture(user.groupIds).flatMap { currentUserGroups =>
cache
.getOrElseUpdate[Html](cacheKey, 1.hours)(
generateStats(
areaIds,
observableOrganisationIds,
observableGroupIds,
creationMinDate,
creationMaxDate,
rights
)
)
.map { html =>
eventService.log(StatsShowed, "Visualise les stats")
Ok(
views.html.stats.page(user, rights)(
formUrl,
html,
groupsThatCanBeFilteredBy = currentUserGroups,
areaIds,
observableOrganisationIds,
observableGroupIds,
organisationIds,
groupIds,
creationMinDate,
creationMaxDate
)
)
.map { html =>
eventService.log(StatsShowed, "Visualise les stats")
Ok(
views.html.stats.page(request.currentUser, request.rights)(
html,
groupsThatCanBeFilteredBy = currentUserGroups,
areaIds,
organisationIds,
groupIds,
creationMinDate,
creationMaxDate
)
).withHeaders(CONTENT_SECURITY_POLICY -> statsCSP)
}
}
).withHeaders(CONTENT_SECURITY_POLICY -> statsCSP)
}
}
}

def allAs(userId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
val userOption = userService.byId(userId)
(request.currentUser.admin, userOption) match {
case (false, Some(user)) =>
eventService.log(
AllAsUnauthorized,
s"L'utilisateur n'a pas de droit d'afficher la vue de l'utilisateur $userId",
involvesUser = Some(user.id)
)
Future(
Unauthorized(
s"Vous n'avez pas le droit de faire ça, vous n'êtes pas administrateur. Vous pouvez contacter l'équipe A+ : ${Constants.supportEmail}"
)
)
case (true, Some(user)) if user.admin =>
eventService.log(
AllAsUnauthorized,
s"L'utilisateur n'a pas de droit d'afficher la vue de l'utilisateur admin $userId",
involvesUser = Some(user.id)
)
Future(
Unauthorized(
s"Vous n'avez pas le droit de faire ça avec un compte administrateur. Vous pouvez contacter l'équipe A+ : ${Constants.supportEmail}"
)
)
case (true, Some(user)) if request.currentUser.areas.intersect(user.areas).nonEmpty =>
LoginAction.readUserRights(user).map { userRights =>
val targetUserId = user.id
withUser(userId) { otherUser: User =>
asUserWithAuthorization(Authorization.canSeeOtherUserNonPrivateViews(otherUser))(
() =>
EventType.AllAsUnauthorized -> s"Accès non autorisé pour voir la liste des demandes de $userId",
errorInvolvesUser = Some(otherUser.id)
) { () =>
LoginAction.readUserRights(otherUser).map { userRights =>
val targetUserId = otherUser.id
val applicationsFromTheArea = List.empty[Application]
eventService
.log(
AllAsShowed,
s"Visualise la vue de l'utilisateur $userId",
involvesUser = Some(user.id)
involvesUser = Some(otherUser.id)
)
val applications = applicationService.allForUserId(
userId = targetUserId,
anonymous = request.currentUser.admin
)
val (closedApplications, openApplications) = applications.partition(_.closed)
Ok(
views.html.myApplications(user, userRights)(
views.html.myApplications(otherUser, userRights)(
myOpenApplications = openApplications,
myClosedApplications = closedApplications,
applicationsFromTheArea = applicationsFromTheArea
)
)
}
case _ =>
eventService.log(AllAsNotFound, s"L'utilisateur $userId n'existe pas")
Future(
BadRequest(
s"L'utilisateur n'existe pas ou vous n'avez pas le droit d'accéder à cette page. Vous pouvez contacter l'équipe A+ : ${Constants.supportEmail}"
)
)
}
}
}

Expand Down
33 changes: 25 additions & 8 deletions app/controllers/GroupController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ case class GroupController @Inject() (
val message = "L’adresse email n'est pas correcte"
eventService.log(EventType.AddUserToGroupBadUserInput, message)
if (originIsGroupPage) withGroup(groupId)(group => editGroupPage(group, err))
else editMyGroupsPage(err)
else editMyGroupsPage(request.currentUser, request.rights, err)
},
data =>
userService
Expand Down Expand Up @@ -130,7 +130,7 @@ case class GroupController @Inject() (
withUser(
userId,
includeDisabled = true,
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être réactivé",
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être réactivé".some,
errorResult = Redirect(redirectPage)
.flashing(
"error" -> ("L’utilisateur n’existe pas dans Administration+. " +
Expand Down Expand Up @@ -173,7 +173,7 @@ case class GroupController @Inject() (
withUser(
userId,
includeDisabled = true,
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être désactivé",
errorMessage = s"L'utilisateur $userId n'existe pas et ne peut pas être désactivé".some,
errorResult = Redirect(redirectPage)
.flashing(
"error" -> ("L’utilisateur n’existe pas dans Administration+. " +
Expand Down Expand Up @@ -218,9 +218,24 @@ case class GroupController @Inject() (
}
}

def showEditMyGroups =
def showEditMyGroups: Action[AnyContent] =
loginAction.async { implicit request =>
editMyGroupsPage(AddUserToGroupFormData.form)
editMyGroupsPage(request.currentUser, request.rights, AddUserToGroupFormData.form)
}

def showEditMyGroupsAs(otherUserId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
withUser(otherUserId) { otherUser: User =>
asUserWithAuthorization(Authorization.canSeeOtherUserNonPrivateViews(otherUser))(
() =>
EventType.MasqueradeUnauthorized -> s"Accès non autorisé pour voir la page mes groupes de $otherUserId",
errorInvolvesUser = Some(otherUser.id)
) { () =>
LoginAction.readUserRights(otherUser).flatMap { userRights =>
editMyGroupsPage(otherUser, userRights, AddUserToGroupFormData.form)
}
}
}
}

def deleteUnusedGroupById(groupId: UUID): Action[AnyContent] =
Expand Down Expand Up @@ -387,19 +402,21 @@ case class GroupController @Inject() (
}

private def editMyGroupsPage(
user: User,
rights: Authorization.UserRights,
addUserForm: Form[AddUserToGroupFormData]
)(implicit request: RequestWithUserData[_]): Future[Result] =
for {
groups <- groupService.byIdsFuture(request.currentUser.groupIds)
groups <- groupService.byIdsFuture(user.groupIds)
users <- userService.byGroupIdsFuture(groups.map(_.id), includeDisabled = true)
applications <- applicationService.allForUserIds(users.map(_.id))
} yield {
eventService.log(EventType.EditMyGroupShowed, "Visualise la modification de ses groupes")
Ok(
views.editMyGroups
.page(
request.currentUser,
request.rights,
user,
rights,
addUserForm,
groups,
users,
Expand Down
16 changes: 11 additions & 5 deletions app/controllers/Operators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,37 @@ object Operators {
def withUser(
userId: UUID,
includeDisabled: Boolean = false,
errorMessage: String = "Tentative d'accès à un utilisateur inexistant",
errorMessage: Option[String] = none,
errorResult: Option[Result] = none
)(
payload: User => Future[Result]
)(implicit request: RequestWithUserData[_]): Future[Result] =
userService
.byId(userId, includeDisabled)
.fold({
eventService.log(UserNotFound, description = errorMessage)
eventService.log(
UserNotFound,
description =
errorMessage.getOrElse(s"Tentative d'accès à un utilisateur inexistant ($userId)"),
involvesUser = Some(userId)
)
Future.successful(
errorResult.getOrElse(NotFound("Utilisateur inexistant"))
errorResult.getOrElse(NotFound(s"L'utilisateur n'existe pas."))
)
})({ user: User => payload(user) })

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

0 comments on commit 6e33f12

Please sign in to comment.