backend/app/controllers/api/Users.scala (111 lines of code) (raw):

package controllers.api import model.frontend.TotpActivation import model.frontend.user.PartialUser import model.manifest.UserWithCollections import model.user.UserPermission.CanPerformAdminOperations import model.user.{UserPermission, UserPermissions} import play.api.libs.json._ import utils._ import utils.attempt._ import utils.auth.UserIdentityRequest import utils.auth.providers.UserProvider import utils.controller.{OptionalAuthApiController, AuthControllerComponents} class Users(override val controllerComponents: AuthControllerComponents, userProvider: UserProvider) extends OptionalAuthApiController with Logging { def listUsers = auth.ApiAction.attempt { req => auth.checkPermission(CanPerformAdminOperations, req) { listUsersAsAdmin().map { usersWithCollections => Ok(Json.obj("users" -> Json.toJson(usersWithCollections))) } }.recoverWith { case MissingPermissionFailure(_) => listUsersAsPunter().map { partialUsers => Ok(Json.obj("users" -> Json.toJson(partialUsers))) } } } def getMyPermissions = auth.ApiAction.attempt { req: UserIdentityRequest[_] => for { permissions <- controllerComponents.users.getPermissions(req.user.username) } yield Ok(Json.toJson(permissions)) } // Create a new user, requires auth and CanManagerUsers permission def createUser(username: String) = auth.ApiAction.attempt(parse.json) { req: UserIdentityRequest[JsValue] => for { user <- auth.checkPermission(CanPerformAdminOperations, req) { userProvider.createUser(username, req.body) } } yield Ok(Json.toJson(user)) } // Register the skeleton of a user def registerUser(username: String) = noAuth.ApiAction.attempt(parse.json) { request => for { _ <- userProvider.registerUser(request.body, Epoch.now) } yield NoContent } def updateUserFullname(username: String) = auth.ApiAction.attempt(parse.json) { req: UserIdentityRequest[JsValue] => for { displayName <- (req.body \ "displayName").validate[String].toAttempt _ <- auth.checkPermission(CanPerformAdminOperations, req) { controllerComponents.users.updateUserDisplayName(username, displayName) } } yield NoContent } def updateUserPassword(username: String) = auth.ApiAction.attempt(parse.json) { req: UserIdentityRequest[JsValue] => for { newPassword <- (req.body \ "password").validate[String].toAttempt _ <- auth.checkPermission(CanPerformAdminOperations, req) { userProvider.updatePassword(username, newPassword) } } yield NoContent } def setPermissions(username: String) = auth.ApiAction.attempt(parse.json) { req: UserIdentityRequest[JsValue] => for { granted <- (req.body \ "permissions" \ "granted").validate[Set[UserPermission]].toAttempt _ <- auth.checkPermission(CanPerformAdminOperations, req) { if(username == req.user.username && !granted.contains(CanPerformAdminOperations)) { // Avoid confusion by revoking your own permission to grant permissions Attempt.Left(IllegalStateFailure(s"Cannot remove CanPerformAdminOperations from yourself")) } else { controllerComponents.users.setPermissions(username, UserPermissions(granted)) } } } yield NoContent } def enrollUser2FA(username: String) = auth.ApiAction.attempt(parse.json) { req: UserIdentityRequest[JsValue] => val time = Epoch.now for { totpActivation <- (req.body \ "totpActivation").validate[TotpActivation].toAttempt _ <- auth.checkPermission(CanPerformAdminOperations, req) { userProvider.enrollUser2FA(username, totpActivation, time) } } yield NoContent } def removeUser2FA(username: String) = auth.ApiAction.attempt { req: UserIdentityRequest[_] => for { _ <- auth.checkPermission(CanPerformAdminOperations, req) { userProvider.removeUser2FA(username) } } yield NoContent } def removeUser(username: String) = auth.ApiAction.attempt { request: UserIdentityRequest[_] => for { _ <- if (username == request.user.username) Attempt.Left(ClientFailure("Cannot delete own user account")) else Attempt.Right(()) _ <- auth.checkPermission(CanPerformAdminOperations, request) { userProvider.removeUser(username) } } yield NoContent } private def listUsersAsPunter(): Attempt[List[PartialUser]] = { controllerComponents.users.listUsers().map { userList => userList.map { case(dbUser, _) => dbUser.toPartial } } } private def listUsersAsAdmin(): Attempt[List[UserWithCollections]] = { controllerComponents.users.listUsers().flatMap { userList => Attempt.sequence(userList.map { case (user, collections) => controllerComponents.users.getPermissions(user.username).map { permissions => val partialUser = user.toPartial UserWithCollections(partialUser.username, partialUser.displayName, collections.map(_.uri.value), permissions) } }) } } }