Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrige les groupes non éditables par les responsables #1045

Merged
merged 1 commit into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 52 additions & 52 deletions app/controllers/GroupController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import java.time.{ZoneId, ZonedDateTime}
import java.util.UUID

import actions.{LoginAction, RequestWithUserData}
import cats.syntax.all._
import Operators._
import javax.inject.{Inject, Singleton}
import models.{Area, Organisation, UserGroup}
import models.{Area, Authorization, Organisation, UserGroup}
import models.formModels.{normalizedOptionalText, normalizedText}
import org.webjars.play.WebJarsUtil
import play.api.Configuration
Expand Down Expand Up @@ -78,12 +79,14 @@ case class GroupController @Inject() (
def editGroup(id: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
withGroup(id) { group: UserGroup =>
if (!group.canHaveUsersAddedBy(request.currentUser)) {
eventService.log(EditGroupUnauthorized, s"Accès non autorisé à l'edition de ce groupe")
Future(
Unauthorized("Vous ne pouvez pas éditer ce groupe : êtes-vous dans la bonne zone ?")
)
} else {
asUserWithAuthorization(Authorization.canEditGroup(group))(
() =>
(
EditGroupUnauthorized,
s"Tentative d'accès non autorisé à l'edition du groupe ${group.id}"
),
Unauthorized("Vous ne pouvez pas éditer ce groupe : êtes-vous dans la bonne zone ?").some
) { () =>
val groupUsers = userService.byGroupIds(List(id), includeDisabled = true)
eventService.log(EditGroupShowed, s"Visualise la vue de modification du groupe")
val isEmpty = groupService.isGroupEmpty(group.id)
Expand Down Expand Up @@ -151,58 +154,55 @@ case class GroupController @Inject() (

def editGroupPost(groupId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
asAdmin(() => EditGroupUnauthorized -> s"Accès non autorisé à l'edition de ce groupe") { () =>
withGroup(groupId) { currentGroup: UserGroup =>
if (not(currentGroup.canHaveUsersAddedBy(request.currentUser))) {
eventService.log(
AddUserToGroupUnauthorized,
s"L'utilisateur ${request.currentUser.id} n'est pas authorisé à ajouter des utilisateurs au groupe ${currentGroup.id}."
)
Future(
Unauthorized("Vous n'êtes pas authorisé à ajouter des utilisateurs à ce groupe.")
)
} else {
addGroupForm(Time.timeZoneParis)
.bindFromRequest()
.fold(
formWithError => {
withGroup(groupId) { currentGroup: UserGroup =>
asUserWithAuthorization(Authorization.canEditGroup(currentGroup))(
() =>
(
EditGroupUnauthorized,
s"Tentative d'édition non autorisée du groupe ${currentGroup.id}"
),
Unauthorized("Vous ne pouvez pas éditer ce groupe : êtes-vous dans la bonne zone ?").some
) { () =>
addGroupForm(Time.timeZoneParis)
.bindFromRequest()
.fold(
formWithError => {
eventService.log(
EditUserGroupError,
s"Tentative d'edition du groupe ${currentGroup.id} avec des erreurs de validation"
)
Future(
Redirect(routes.GroupController.editGroup(groupId)).flashing(
"error" -> s"Impossible de modifier le groupe (erreur de formulaire) : ${formWithError.errors.mkString}"
)
)
},
group => {
val newGroup = group.copy(id = groupId)
if (groupService.edit(newGroup)) {
eventService.log(
EditUserGroupError,
s"Tentative d'edition du groupe ${currentGroup.id} avec des erreurs de validation"
UserGroupEdited,
s"Groupe édité ${currentGroup.toDiffLogString(newGroup)}"
)
Future(
Redirect(routes.GroupController.editGroup(groupId)).flashing(
"error" -> s"Impossible de modifier le groupe (erreur de formulaire) : ${formWithError.errors.mkString}"
)
Redirect(routes.GroupController.editGroup(groupId))
.flashing("success" -> "Groupe modifié")
)
},
group => {
val newGroup = group.copy(id = groupId)
if (groupService.edit(newGroup)) {
eventService.log(
UserGroupEdited,
s"Groupe édité ${currentGroup.toDiffLogString(newGroup)}"
} else {
eventService
.log(
EditUserGroupError,
s"Impossible de modifier le groupe dans la BDD ${currentGroup.toDiffLogString(newGroup)}"
)
Future(
Redirect(routes.GroupController.editGroup(groupId))
.flashing("success" -> "Groupe modifié")
)
} else {
eventService
.log(
EditUserGroupError,
s"Impossible de modifier le groupe dans la BDD ${currentGroup.toDiffLogString(newGroup)}"
Future(
Redirect(routes.GroupController.editGroup(groupId))
.flashing(
"error" -> "Impossible de modifier le groupe: erreur en base de donnée"
)
Future(
Redirect(routes.GroupController.editGroup(groupId))
.flashing(
"error" -> "Impossible de modifier le groupe: erreur en base de donnée"
)
)
}
)
}
)
}
}
)
}
}
}
Expand Down
27 changes: 16 additions & 11 deletions app/controllers/UserController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,14 @@ case class UserController @Inject() (
def addPost(groupId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
withGroup(groupId) { group: UserGroup =>
if (!group.canHaveUsersAddedBy(request.currentUser)) {
eventService.log(PostAddUserUnauthorized, "Accès non autorisé à l'admin des utilisateurs")
Future(Unauthorized("Vous n'avez pas le droit de faire ça"))
} else {
asUserWithAuthorization(Authorization.canEditGroup(group))(
() =>
(
PostAddUserUnauthorized,
s"Tentative non autorisée d'ajout d'utilisateurs au groupe ${group.id}"
),
Unauthorized("Vous ne pouvez pas ajouter des utilisateurs à ce groupe.").some
) { () =>
addUsersForm
.bindFromRequest()
.fold(
Expand Down Expand Up @@ -942,13 +946,14 @@ case class UserController @Inject() (
def add(groupId: UUID): Action[AnyContent] =
loginAction.async { implicit request =>
withGroup(groupId) { group: UserGroup =>
if (!group.canHaveUsersAddedBy(request.currentUser)) {
eventService.log(
ShowAddUserUnauthorized,
s"Accès non autorisé à l'admin des utilisateurs du groupe $groupId"
)
Future(Unauthorized("Vous n'avez pas le droit de faire ça"))
} else {
asUserWithAuthorization(Authorization.canEditGroup(group))(
() =>
(
ShowAddUserUnauthorized,
s"Tentative non autorisée d'accès à l'ajout d'utilisateurs dans le groupe ${group.id}"
),
Unauthorized("Vous ne pouvez pas ajouter des utilisateurs à ce groupe.").some
) { () =>
val rows =
request
.getQueryString(Keys.QueryParam.rows)
Expand Down
24 changes: 21 additions & 3 deletions app/models/Authorization.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@ object Authorization {

type Check = UserRights => Boolean

def forall[A](list: List[A], fn: A => Check): Check =
rights => list.forall(fn(_)(rights))

def atLeastOneIsAuthorized(checks: Check*): Check =
rights => checks.exists(_(rights))

def allMustBeAuthorized(checks: Check*): Check =
rights => checks.forall(_(rights))
forall[Check](checks.toList, identity)

def isInGroup(groupId: UUID): Check =
_.rights.exists {
Expand Down Expand Up @@ -115,6 +118,12 @@ object Authorization {
case _ => false
}

def isManagerOfGroup(groupId: UUID): Check =
_.rights.exists {
case UserRight.ManagerOfGroups(managedGroups) if managedGroups.contains(groupId) => true
case _ => false
}

def isObserver: Check =
_.rights.exists {
case UserRight.ObserverOfOrganisations(organisations) => organisations.nonEmpty
Expand Down Expand Up @@ -163,8 +172,15 @@ object Authorization {
def canEnableOtherUser(otherUser: User): Check =
atLeastOneIsAuthorized(isAdmin, atLeastOneIsAuthorized(otherUser.groupIds.map(isInGroup): _*))

def canEditGroups: Check =
atLeastOneIsAuthorized(isAdmin, isManager)
def canEditGroup(group: UserGroup): Check =
atLeastOneIsAuthorized(
forall(group.areaIds, isAdminOfArea),
isManagerOfGroup(group.id)
)

/** For organisation & areas. */
def canEditGroupAnyField(group: UserGroup): Check =
forall(group.areaIds, isAdminOfArea)

def canSeeUsers: Check =
atLeastOneIsAuthorized(isAdmin, isManager, isObserver)
Expand All @@ -175,6 +191,8 @@ object Authorization {

def canCreateSignups: Check = isAdmin

def canEditSupportMessages: Check = isAdmin

//
// Authorizations concerning Applications
//
Expand Down
5 changes: 0 additions & 5 deletions app/models/UserGroup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ case class UserGroup(
internalSupportComment: Option[String]
) {

def canHaveUsersAddedBy(user: User): Boolean =
(user.groupAdmin && user.groupIds.contains(id)) || (user.admin && areaIds.forall(
user.areas.contains
))

lazy val organisationSetOrDeducted: Option[Organisation] =
organisation
.flatMap(Organisation.byId)
Expand Down
2 changes: 1 addition & 1 deletion app/views/allUsersByGroup.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
<a href="@routes.UserController.editUser(user.id)" target="_blank" >@user.name (@user.qualite)</a> /
} <hr>
}
@if(Authorization.canEditGroups(currentUserRights)) {
@if(Authorization.canEditGroup(userGroup)(currentUserRights)) {
@helper.form(routes.UserController.add(userGroup.id), "method" -> "get") {
@helper.CSRF.formField
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width: unset; margin-right: 24px;">
Expand Down
42 changes: 34 additions & 8 deletions app/views/editGroup.scala.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
@import models.Authorization

@(currentUser: User, currentUserRights: Authorization.UserRights)(userGroup: UserGroup, groupUsers: List[User], isEmpty: Boolean)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, request: RequestHeader)


@main(currentUser, currentUserRights)(s"Gestion du groupe ${userGroup.name} - ${userGroup.areaIds.flatMap(Area.fromId).map(_.name).mkString(", ")}") {
<link rel="stylesheet" media="screen,print" href='@routes.Assets.versioned("stylesheets/newForm.css")'>
<link rel="stylesheet" media="screen,print" href='@routes.Assets.versioned("stylesheets/auto-complete.css")'>
}{

@defining(Authorization.canEditGroup(userGroup)(currentUserRights)) { canEdit =>
@defining(Authorization.canEditGroupAnyField(userGroup)(currentUserRights)) { canEditAny =>

<div class="mdl-cell mdl-cell--12-col mdl-grid--no-spacing">
<span class="mdl-cell mdl-cell--12-col ariane">Territoires : @for(areaId <- userGroup.areaIds) {
<a href="@routes.UserController.all(areaId)#group-@userGroup.id">@{Area.fromId(areaId).get.name}</a>
} / Groupe : @userGroup.name </span>
@helper.form(routes.GroupController.editGroupPost(userGroup.id), "method" -> "post") {
@helper.CSRF.formField
<div class="mdl-cell mdl-cell--12-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="name" name="name" value="@userGroup.name" @if(!currentUser.admin) { disabled } maxlength="@models.UserGroup.nameMaxLength">
<input class="mdl-textfield__input" type="text" id="name" name="name" value="@userGroup.name" @if(!canEdit) { disabled } maxlength="@models.UserGroup.nameMaxLength">
<label class="mdl-textfield__label" for="name">Nom du groupe</label>
</div>
<div class="mdl-cell mdl-cell--12-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="description" name="description" value="@userGroup.description" @if(!currentUser.admin) { disabled }>
<input class="mdl-textfield__input" type="text" id="description" name="description" value="@userGroup.description" @if(!canEdit) { disabled }>
<label class="mdl-textfield__label" for="description">Description succincte</label>
</div>

<div class="mdl-cell mdl-cell--12-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<textarea class="mdl-textfield__input single--height-auto" type="text" rows="3" id="publicNote" name="publicNote">@userGroup.publicNote</textarea>
<textarea class="mdl-textfield__input single--height-auto" type="text" rows="3" id="publicNote" name="publicNote" @if(!canEdit) { disabled }>@userGroup.publicNote</textarea>
<label class="mdl-textfield__label" for="publicNote">Description détaillée</label>
</div>

Expand All @@ -33,27 +39,42 @@
</div>
}
<div class="mdl-cell mdl-cell--12-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="email" name="email" value="@userGroup.email.getOrElse("")" @if(!currentUser.admin) { disabled } maxlength="200">
<input class="mdl-textfield__input" type="text" id="email" name="email" value="@userGroup.email.getOrElse("")" @if(!canEdit) { disabled } maxlength="200">
<label class="mdl-textfield__label" for="email">Email du groupe (BAL générique pour inscription et notifications)</label>
</div>
<div class="mdl-cell mdl-cell--12-col">
<select id="organisation" name="organisation">
<select id="organisation" name="organisation" @if(!canEditAny) {disabled}>
<option @if(userGroup.organisationSetOrDeducted.isEmpty){selected} style="font-weight: bold" value="">Organisme non-référencé</option>
@for(organisation <- Organisation.all) {
<option value="@organisation.id.id" @if(userGroup.organisationSetOrDeducted.map(_.id).contains[Organisation.Id](organisation.id)){selected}>@organisation.shortName : @organisation.name</option>
}
</select>
@if(!canEditAny) {
<select id="organisation-hidden" name="organisation" class="hidden">
<option value="">Organisme non-référencé</option>
@for(organisation <- Organisation.all) {
<option value="@organisation.id.id" @if(userGroup.organisationSetOrDeducted.map(_.id).contains[Organisation.Id](organisation.id)){selected}>@organisation.shortName : @organisation.name</option>
}
</select>
}
</div>
<div class="mdl-cell mdl-cell--12-col">
<select id="area-ids" name="area-ids[]" multiple size="5" class="use-slimselect">
<select id="area-ids" name="area-ids[]" multiple size="5" class="use-slimselect" @if(!canEditAny) {disabled}>
@for(area <- currentUser.areas.flatMap(Area.fromId)) {
<option value="@area.id" @if(userGroup.areaIds.contains(area.id)) { selected }>@area.name</option>
}
</select>
@if(!canEditAny) {
<select id="area-ids" name="area-ids[]" multiple size="5" class="hidden">
@for(area <- Area.all) {
<option value="@area.id" @if(userGroup.areaIds.contains(area.id)) { selected }>@area.name</option>
}
</select>
}
</div>


@if(currentUser.admin) {
@if(Authorization.canEditSupportMessages(currentUserRights)) {

<div class="mdl-cell mdl-cell--12-col mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<textarea class="mdl-textfield__input single--height-auto" type="text" rows="3" id="internalSupportComment" name="internalSupportComment">@userGroup.internalSupportComment</textarea>
Expand All @@ -67,7 +88,7 @@
value="@userGroup.internalSupportComment">
}

@if(currentUser.admin) {
@if(canEdit) {
<div class="mdl-cell mdl-cell--12-col">
<button class="mdl-button mdl-js-button mdl-button--raised" type="submit">
Editer le groupe
Expand Down Expand Up @@ -104,6 +125,11 @@ <h4 class="mdl-dialog__title">Veuillez confirmer la suppression du groupe.</h4>
<button type="button" class="mdl-button mdl-button" id="dialog-delete-group-confirm" data-uuid="@userGroup.id">Confirmer</button>
</div>
</dialog>



}
}
}{
<script src='@routes.Assets.versioned("javascripts/auto-complete.min.js")'></script>
}
2 changes: 1 addition & 1 deletion app/views/editMyGroups.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
@for(userGroup <- userGroups.sortBy(_.name)) {
@defining(users.filter(_.groupIds.contains(userGroup.id))) { groupUsers =>
@display(groupUsers, userGroup.id) {
@if(Authorization.canEditGroups(currentUserRights)) {
@if(Authorization.canEditGroup(userGroup)(currentUserRights)) {
<a href="@routes.GroupController.editGroup(userGroup.id)">@userGroup.name</a>
} else {
@userGroup.name
Expand Down