From 350f0bd10e65c3d60dfab546d3a375ff3990a614 Mon Sep 17 00:00:00 2001 From: vlad Date: Wed, 3 Aug 2016 19:58:45 -0700 Subject: Finer grained permission-control --- src/main/scala/com/drivergrp/core/auth.scala | 76 ++++++++++++++++++------ src/main/scala/com/drivergrp/core/database.scala | 4 ++ src/main/scala/com/drivergrp/core/rest.scala | 6 +- 3 files changed, 66 insertions(+), 20 deletions(-) (limited to 'src/main/scala/com') diff --git a/src/main/scala/com/drivergrp/core/auth.scala b/src/main/scala/com/drivergrp/core/auth.scala index 8722d6a..eed40ef 100644 --- a/src/main/scala/com/drivergrp/core/auth.scala +++ b/src/main/scala/com/drivergrp/core/auth.scala @@ -2,7 +2,16 @@ package com.drivergrp.core object auth { - trait Permission + sealed trait Permission + case object CanSeeUser extends Permission + case object CanSeeAssay extends Permission + case object CanSeeReport extends Permission + case object CanCreateReport extends Permission + case object CanEditReport extends Permission + case object CanEditReviewingReport extends Permission + case object CanSignOutReport extends Permission + case object CanShareReportWithPatient extends Permission + case object CanAssignRoles extends Permission trait Role { val id: Id[Role] @@ -11,6 +20,43 @@ object auth { def hasPermission(permission: Permission): Boolean = false } + case object ObserverRole extends Role { + val id = Id(1L) + val name = Name("observer") + + override def hasPermission(permission: Permission): Boolean = + Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport).contains(permission) + } + + case object PatientRole extends Role { + val id = Id(2L) + val name = Name("patient") + } + + case object CuratorRole extends Role { + val id = Id(3L) + val name = Name("curator") + + override def hasPermission(permission: Permission): Boolean = + Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport, CanEditReport).contains(permission) + } + + case object PathologistRole extends Role { + val id = Id(4L) + val name = Name("pathologist") + + override def hasPermission(permission: Permission): Boolean = + Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport, CanEditReport, CanSignOutReport, CanEditReviewingReport) + .contains(permission) + } + + case object AdministratorRole extends Role { + val id = Id(5L) + val name = Name("administrator") + + override def hasPermission(permission: Permission): Boolean = true + } + trait User { def id: Id[User] def roles: Set[Role] @@ -22,31 +68,27 @@ object auth { final case class AuthToken(value: Base64[Macaroon]) + def extractUser(authToken: AuthToken): User = { + new User() { + override def id: Id[User] = Id[User](1L) + override def roles: Set[Role] = Set(PathologistRole) + } + + // TODO: or reject(ValidationRejection(s"Wasn't able to extract user for the token provided")) if none + } + object directives { import akka.http.scaladsl.server._ import Directives._ val AuthenticationTokenHeader = "WWW-Authenticate" - type UserExtractor = AuthToken => Option[User] - - def authorize(role: Role)(implicit userExtractor: UserExtractor): Directive1[Id[User]] = { + def authorize(permission: Permission): Directive1[AuthToken] = { headerValueByName(AuthenticationTokenHeader).flatMap { tokenValue => val token = AuthToken(Base64[Macaroon](tokenValue)) - userExtractor(token) match { - case Some(user) => - if (user.roles.contains(role)) provide(user.id: Id[User]) - else reject(ValidationRejection(s"User does not have the required ${role.name} role")) - case None => - reject(ValidationRejection(s"Wasn't able to extract user for the token provided")) - } - } - } - - def extractToken: Directive1[AuthToken] = { - headerValueByName(AuthenticationTokenHeader).flatMap { token => - provide(AuthToken(Base64[Macaroon](token))) + if (extractUser(token).roles.exists(_.hasPermission(permission))) provide(token) + else reject(ValidationRejection(s"User does not have the required permission $permission")) } } } diff --git a/src/main/scala/com/drivergrp/core/database.scala b/src/main/scala/com/drivergrp/core/database.scala index beabbff..581c5de 100644 --- a/src/main/scala/com/drivergrp/core/database.scala +++ b/src/main/scala/com/drivergrp/core/database.scala @@ -1,5 +1,7 @@ package com.drivergrp.core +import com.drivergrp.core.time.Time + import scala.concurrent.Future import slick.backend.DatabaseConfig import slick.driver.JdbcProfile @@ -33,6 +35,8 @@ object database { implicit def nameColumnType[T] = MappedColumnType.base[Name[T], String](name => name: String, Name[T](_)) + + implicit val timeColumnType = MappedColumnType.base[Time, Long](time => time.millis, Time(_)) } trait DatabaseObject extends IdColumnTypes { diff --git a/src/main/scala/com/drivergrp/core/rest.scala b/src/main/scala/com/drivergrp/core/rest.scala index ebb2640..a08dc73 100644 --- a/src/main/scala/com/drivergrp/core/rest.scala +++ b/src/main/scala/com/drivergrp/core/rest.scala @@ -30,16 +30,16 @@ object rest { this.majorVersion === otherVersion.majorVersion } - type Service = AnyRef + trait Service trait ServiceTransport { def sendRequest(authToken: AuthToken)(requestStub: HttpRequest): Future[Unmarshal[ResponseEntity]] } - trait ServiceDiscovery[T <: Service] { + trait ServiceDiscovery { - def discover(serviceName: Name[Service], version: ServiceVersion): T + def discover[T <: Service](serviceName: Name[Service], version: ServiceVersion): T } class HttpRestServiceTransport(actorSystem: ActorSystem, executionContext: ExecutionContext, -- cgit v1.2.3