Skip to content

Commit

Permalink
Utilise Authorization au lieu du boolean User.helper (#1093)
Browse files Browse the repository at this point in the history
  • Loading branch information
niladic authored Jul 27, 2021
1 parent 2871447 commit 80e2b70
Show file tree
Hide file tree
Showing 18 changed files with 255 additions and 140 deletions.
8 changes: 3 additions & 5 deletions app/controllers/ApplicationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import play.api.data.validation.Constraints._
import play.api.libs.ws.WSClient
import play.api.mvc._
import play.twirl.api.Html
import serializers.{AttachmentHelper, DataModel, Keys}
import serializers.{AttachmentHelper, Keys}
import services._
import views.stats.StatsData

Expand Down Expand Up @@ -145,9 +145,7 @@ case class ApplicationController @Inject() (
val usersInThoseGroups = userService.byGroupIds(visibleGroups.map(_.id))
// Note: we don't care about users who are in several areas
val coworkers = usersInThoseGroups
.filter(user =>
user.helper && user.groupIds.toSet.intersect(currentUser.groupIds.toSet).nonEmpty
)
.filter(user => Authorization.canAddUserAsCoworkerToNewApplication(user)(rights))
.filterNot(user => user.id === currentUser.id)
// This could be optimized by doing only one SQL query
val instructorsOfGroups = usersInThoseGroups.filter(_.instructor)
Expand Down Expand Up @@ -318,7 +316,7 @@ case class ApplicationController @Inject() (
applicationData.selectedSubject.contains[String](applicationData.subject),
category = applicationData.category,
files = newAttachments ++ pendingAttachments,
mandatType = DataModel.Application.MandatType
mandatType = dataModels.Application.MandatType
.dataModelDeserialization(applicationData.mandatType),
mandatDate = Some(applicationData.mandatDate),
invitedGroupIdsAtCreation = applicationData.groups
Expand Down
23 changes: 2 additions & 21 deletions app/controllers/UserController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ case class UserController @Inject() (
Time.formatPatternFr(user.creationDate, "dd-MM-YYYY-HHhmm"),
if (user.sharedAccount) "Compte Partagé" else " ",
if (user.sharedAccount) user.name else " ",
if (user.helper) "Aidant" else " ",
user.helperRoleName.getOrElse(""),
if (user.instructor) "Instructeur" else " ",
if (user.groupAdmin) "Responsable" else " ",
if (user.expert) "Expert" else " ",
Expand Down Expand Up @@ -321,26 +321,7 @@ case class UserController @Inject() (
eventService.log(UserNotFound, s"L'utilisateur $userId n'existe pas")
Future(NotFound("Nous n'avons pas trouvé cet utilisateur"))
case Some(user) if Authorization.canSeeOtherUser(user)(request.rights) =>
val form = editUserForm.fill(
EditUserFormData(
id = user.id,
firstName = user.firstName,
lastName = user.lastName,
name = user.name,
qualite = user.qualite,
email = user.email,
helper = user.helper,
instructor = user.instructor,
areas = user.areas,
groupAdmin = user.groupAdmin,
disabled = user.disabled,
groupIds = user.groupIds,
phoneNumber = user.phoneNumber,
observableOrganisationIds = user.observableOrganisationIds,
sharedAccount = user.sharedAccount,
internalSupportComment = user.internalSupportComment
)
)
val form = editUserForm.fill(EditUserFormData.fromUser(user))
val groups = groupService.allGroups
val unused = not(isAccountUsed(user))
val Token(tokenName, tokenValue) = CSRF.getToken.get
Expand Down
17 changes: 16 additions & 1 deletion app/models/Authorization.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ object Authorization {
case _ => false
}

def isInOneOfGroups(groupIds: Set[UUID]): Check =
_.rights.exists {
case UserRight.IsInGroups(groups) if groups.intersect(groupIds).nonEmpty => true
case _ => false
}

def isAdmin: Check =
_.rights.exists {
case UserRight.AdminOfAreas(_) => true
Expand All @@ -88,7 +94,7 @@ object Authorization {
case _ => false
}

def isHelper: Check =
private def isHelper: Check =
_.rights.exists {
case UserRight.Helper => true
case _ => false
Expand Down Expand Up @@ -153,6 +159,9 @@ object Authorization {
// Authorizations concerning User/UserGroup
//

def canSeeExperimentalAdminFeatures: Check =
isAdmin

// TODO: weird...
def userCanBeEditedBy(editorUser: User): Check =
_ => editorUser.admin && editorUser.areas.intersect(editorUser.areas).nonEmpty
Expand Down Expand Up @@ -197,6 +206,12 @@ object Authorization {
// Authorizations concerning Applications
//

def canCreateApplication: Check =
isHelper

def canAddUserAsCoworkerToNewApplication(otherUser: User): Check =
rights => otherUser.helper && isInOneOfGroups(otherUser.groupIds.toSet)(rights)

def canSeeApplicationsAsAdmin: Check =
atLeastOneIsAuthorized(isAdmin, isManager)

Expand Down
13 changes: 12 additions & 1 deletion app/models/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ case class User(
name: String,
qualite: String,
email: String,
helper: Boolean,
private[models] val helper: Boolean,
instructor: Boolean,
// TODO: `private[models]` so we cannot check it without going through authorization
admin: Boolean,
Expand Down Expand Up @@ -69,6 +69,17 @@ case class User(
phoneNumber = phoneNumber
)

lazy val helperRoleName: Option[String] = helper.some.filter(identity).map(_ => "Aidant")

lazy val instructorRoleName: Option[String] =
instructor.some.filter(identity).map(_ => "Instructeur")

lazy val groupAdminRoleName: Option[String] =
groupAdmin.some.filter(identity).map(_ => "Responsable")

lazy val adminRoleName: Option[String] = admin.some.filter(identity).map(_ => "Admin")
lazy val disabledRoleName: Option[String] = disabled.some.filter(identity).map(_ => "Désactivé")

lazy val firstNameLog: String = firstName.map(withQuotes).getOrElse("<vide>")
lazy val lastNameLog: String = lastName.map(withQuotes).getOrElse("<vide>")
lazy val nameLog: String = withQuotes(name)
Expand Down
95 changes: 92 additions & 3 deletions app/serializers/DataModel.scala → app/models/dataModels.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package serializers
package models

import java.time.{Instant, ZonedDateTime}
import java.util.UUID

import anorm.SqlMappingError
import helper.PlayFormHelper
import helper.{PlayFormHelper, Time}
import models.Answer
import models.Answer.AnswerType
import models.Application.{MandatType, SeenByUser}
Expand All @@ -15,7 +15,7 @@ import serializers.Anorm.columnToJson
import serializers.JsonFormats.mapUUIDFormat

/** Only to serialize/deserialize in PG. */
object DataModel {
object dataModels {

object Answer {

Expand Down Expand Up @@ -194,4 +194,93 @@ object DataModel {

}

object UserRow {

def fromUser(user: User, groupsWhichCannotHaveInstructors: Set[UUID]): UserRow = {
val isInstructor = user.instructor &&
groupsWhichCannotHaveInstructors.intersect(user.groupIds.toSet).isEmpty

UserRow(
id = user.id,
key = user.key,
firstName = user.firstName,
lastName = user.lastName,
name = user.name,
qualite = user.qualite,
email = user.email,
helper = user.helper,
instructor = isInstructor,
admin = user.admin,
areas = user.areas.distinct,
creationDate = user.creationDate.toInstant,
communeCode = user.communeCode,
groupAdmin = user.groupAdmin,
disabled = user.disabled,
expert = user.expert,
groupIds = user.groupIds.distinct,
cguAcceptationDate = user.cguAcceptationDate.map(_.toInstant),
newsletterAcceptationDate = user.newsletterAcceptationDate.map(_.toInstant),
phoneNumber = user.phoneNumber,
observableOrganisationIds = user.observableOrganisationIds.distinct.map(_.id),
sharedAccount = user.sharedAccount,
internalSupportComment = user.internalSupportComment,
)
}

}

case class UserRow(
id: UUID,
key: String,
firstName: Option[String],
lastName: Option[String],
name: String,
qualite: String,
email: String,
helper: Boolean,
instructor: Boolean,
admin: Boolean,
areas: List[UUID],
creationDate: Instant,
communeCode: String,
groupAdmin: Boolean,
disabled: Boolean,
expert: Boolean,
groupIds: List[UUID],
cguAcceptationDate: Option[Instant],
newsletterAcceptationDate: Option[Instant],
phoneNumber: Option[String],
observableOrganisationIds: List[String],
sharedAccount: Boolean,
internalSupportComment: Option[String]
) {

def toUser: User = User(
id = id,
key = key,
firstName = firstName,
lastName = lastName,
name = name,
qualite = qualite,
email = email,
helper = helper,
instructor = instructor,
admin = admin,
areas = areas,
creationDate = creationDate.atZone(Time.timeZoneParis),
communeCode = communeCode,
groupAdmin = groupAdmin,
disabled = disabled,
expert = expert,
groupIds = groupIds,
cguAcceptationDate = cguAcceptationDate.map(_.atZone(Time.timeZoneParis)),
newsletterAcceptationDate = newsletterAcceptationDate.map(_.atZone(Time.timeZoneParis)),
phoneNumber = phoneNumber,
observableOrganisationIds = observableOrganisationIds.map(Organisation.Id.apply),
sharedAccount = sharedAccount,
internalSupportComment = internalSupportComment
)

}

}
24 changes: 24 additions & 0 deletions app/models/formModels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,30 @@ object formModels {
sharedAccount: Boolean
)

object EditUserFormData {

def fromUser(user: User): EditUserFormData =
EditUserFormData(
id = user.id,
firstName = user.firstName,
lastName = user.lastName,
name = user.name,
qualite = user.qualite,
email = user.email,
helper = user.helper,
instructor = user.instructor,
areas = user.areas,
groupAdmin = user.groupAdmin,
disabled = user.disabled,
groupIds = user.groupIds,
phoneNumber = user.phoneNumber,
observableOrganisationIds = user.observableOrganisationIds,
sharedAccount = user.sharedAccount,
internalSupportComment = user.internalSupportComment
)

}

case class EditUserFormData(
id: UUID,
firstName: Option[String],
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/JsonFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object JsonFormats {
//
// Mandat
//
import serializers.DataModel.SmsFormats._
import models.dataModels.SmsFormats._

implicit val mandatIdReads: Reads[Mandat.Id] =
implicitly[Reads[UUID]].map(Mandat.Id.apply)
Expand Down
11 changes: 5 additions & 6 deletions app/services/ApplicationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import helper.Time
import javax.inject.Inject
import models.Application.SeenByUser
import models.Authorization.UserRights
import models.{Answer, Application, Authorization, Error, EventType}
import models.{dataModels, Answer, Application, Authorization, Error, EventType}
import org.postgresql.util.PGobject
import play.api.db.Database
import play.api.libs.json.Json
import play.api.libs.json.Json.toJson
import serializers.DataModel

import scala.Option.empty
import scala.concurrent.Future
Expand All @@ -36,9 +35,9 @@ class ApplicationService @Inject() (
// throws exception "AnormException: 'mandat_type' not found, available columns: ..."
implicit val mandatTypeAnormParser: anorm.Column[Option[Application.MandatType]] =
implicitly[anorm.Column[Option[String]]]
.map(_.flatMap(DataModel.Application.MandatType.dataModelDeserialization))
.map(_.flatMap(dataModels.Application.MandatType.dataModelDeserialization))

import serializers.DataModel.Answer._
import dataModels.Answer._

implicit val answerListParser: anorm.Column[List[Answer]] =
nonNull { (value, meta) =>
Expand All @@ -55,7 +54,7 @@ class ApplicationService @Inject() (
}
}

import DataModel.Application.SeenByUser._
import dataModels.Application.SeenByUser._

private val simpleApplication: RowParser[Application] = Macro
.parser[Application](
Expand Down Expand Up @@ -256,7 +255,7 @@ class ApplicationService @Inject() (
key.toString -> value
})
val mandatType =
newApplication.mandatType.map(DataModel.Application.MandatType.dataModelSerialization)
newApplication.mandatType.map(dataModels.Application.MandatType.dataModelSerialization)
SQL"""
INSERT INTO application (
id,
Expand Down
2 changes: 1 addition & 1 deletion app/services/MandatService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import cats.syntax.all._
import helper.{PlayFormHelper, Time}
import javax.inject.Inject
import models.Authorization.UserRights
import models.dataModels.SmsFormats._
import models.mandat.{Mandat, SmsMandatInitiation}
import models.{Authorization, Error, EventType, Sms, User}
import play.api.db.Database
import play.api.libs.json.{JsValue, Json}
import serializers.DataModel.SmsFormats._

import scala.concurrent.Future
import scala.util.Try
Expand Down
2 changes: 1 addition & 1 deletion app/services/SmsApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ object SmsApi {
ws: WSClient,
implicit val executionContext: ExecutionContext
) extends SmsApi {
import serializers.DataModel.SmsFormats._
import models.dataModels.SmsFormats._

private val serverPort: String =
Option(System.getProperty("http.port"))
Expand Down
Loading

0 comments on commit 80e2b70

Please sign in to comment.