From 3008753cf543caaecb7d0e325c9f4473ad8a0322 Mon Sep 17 00:00:00 2001 From: vlad Date: Tue, 2 Aug 2016 10:54:00 -0700 Subject: Domain model responsible for auth is in core + Akka-http auth directives + More specific REST service API --- src/main/scala/com/drivergrp/core/auth.scala | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/main/scala/com/drivergrp/core/auth.scala (limited to 'src/main/scala/com/drivergrp/core/auth.scala') diff --git a/src/main/scala/com/drivergrp/core/auth.scala b/src/main/scala/com/drivergrp/core/auth.scala new file mode 100644 index 0000000..84d943d --- /dev/null +++ b/src/main/scala/com/drivergrp/core/auth.scala @@ -0,0 +1,100 @@ +package com.drivergrp.core + +object auth { + + final case class FullName[+T](firstName: Name[T], middleName: Name[T], lastName: Name[T]) + + final case class Email(username: String, domain: String) { + override def toString = username + "@" + domain + } + + trait Role { + val id: Id[Role] + val name: Name[Role] + + def canEditReport: Boolean = false + def canSignOffReport: Boolean = false + def canAssignRoles: Boolean = false + } + + case object ObserverRole extends Role { + val id = Id(1L) + val name = Name("observer") + } + + 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 canEditReport: Boolean = true + } + + case object PathologistRole extends Role { + val id = Id(4L) + val name = Name("pathologist") + + override def canEditReport: Boolean = true + override def canSignOffReport: Boolean = true + } + + case object AdministratorRole extends Role { + val id = Id(5L) + val name = Name("administrator") + + override def canEditReport: Boolean = true + override def canSignOffReport: Boolean = true + override def canAssignRoles: Boolean = true + } + + final case class Avatar(id: Id[Avatar], name: Name[Avatar]) + + final case class User(id: Id[User], name: FullName[User], email: Email, avatar: Option[Avatar], roles: Set[Role]) + + val TestUser = User(Id[User](1L), + FullName[User](Name("James"), Name("Dewey"), Name("Watson")), + Email("j.watson", "uchicago.edu"), + Some(Avatar(Id[Avatar](1L), Name[Avatar]("Coolface"))), + Set(PathologistRole)) + + final case class Macaroon(value: String) + + final case class Base64[T](value: String) + + final case class AuthToken(value: Base64[Macaroon]) + + object directives { + import akka.http.scaladsl.server._ + import Directives._ + + val AuthenticationTokenHeader = "WWW-Authenticate" + + def authorize(role: Role): Directive1[Id[User]] = { + headerValueByName(AuthenticationTokenHeader).flatMap { tokenValue => + val token = AuthToken(Base64[Macaroon](tokenValue)) + + extractUser(token) match { + case Some(user) => + if (user.roles.contains(role)) provide(user.id) + 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))) + } + } + + def extractUser(authToken: AuthToken): Option[User] = { + Some(TestUser) + } + } +} -- cgit v1.2.3