aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvlad <vlad@driver.xyz>2017-02-02 18:52:29 -0500
committervlad <vlad@driver.xyz>2017-02-02 18:52:29 -0500
commit51f7f400cab116c2511df1d27ca4edfc3cb0cbc3 (patch)
tree659a817ac86e45a6c993e390251cf04d936a4e28
parent8a525b723ea3cc9388a5399126f540616cd5619e (diff)
downloaddriver-core-51f7f400cab116c2511df1d27ca4edfc3cb0cbc3.tar.gz
driver-core-51f7f400cab116c2511df1d27ca4edfc3cb0cbc3.tar.bz2
driver-core-51f7f400cab116c2511df1d27ca4edfc3cb0cbc3.zip
Removing permissions from user and role as permission is something to check not something to have now
Adding couple handy case classes
-rw-r--r--src/main/scala/xyz/driver/core/auth.scala20
-rw-r--r--src/main/scala/xyz/driver/core/core.scala2
-rw-r--r--src/main/scala/xyz/driver/core/generators.scala2
-rw-r--r--src/main/scala/xyz/driver/core/json.scala9
-rw-r--r--src/main/scala/xyz/driver/core/rest.scala96
-rw-r--r--src/test/scala/xyz/driver/core/AuthTest.scala22
6 files changed, 87 insertions, 64 deletions
diff --git a/src/main/scala/xyz/driver/core/auth.scala b/src/main/scala/xyz/driver/core/auth.scala
new file mode 100644
index 0000000..a9f52e5
--- /dev/null
+++ b/src/main/scala/xyz/driver/core/auth.scala
@@ -0,0 +1,20 @@
+package xyz.driver.core
+
+object auth {
+
+ trait Permission
+
+ final case class Role(id: Id[Role], name: Name[Role])
+
+ trait User {
+ def id: Id[User]
+ def roles: Set[Role]
+ }
+
+ final case class BasicUser(id: Id[User], roles: Set[Role]) extends User
+
+ final case class AuthToken(value: String)
+ final case class RefreshToken(value: String)
+
+ final case class PasswordHash(value: String)
+}
diff --git a/src/main/scala/xyz/driver/core/core.scala b/src/main/scala/xyz/driver/core/core.scala
index db2af95..341d991 100644
--- a/src/main/scala/xyz/driver/core/core.scala
+++ b/src/main/scala/xyz/driver/core/core.scala
@@ -65,4 +65,6 @@ package core {
implicit def revisionEqual[T]: Equal[Revision[T]] = Equal.equal[Revision[T]](_.id == _.id)
}
+
+ final case class Base64(value: String)
}
diff --git a/src/main/scala/xyz/driver/core/generators.scala b/src/main/scala/xyz/driver/core/generators.scala
index d532ae3..45c39fc 100644
--- a/src/main/scala/xyz/driver/core/generators.scala
+++ b/src/main/scala/xyz/driver/core/generators.scala
@@ -56,7 +56,7 @@ object generators {
Time(scala.math.max(oneTime.millis, anotherTime.millis)))
}
- def nextDate(): Date = nextTime.toDate(java.util.TimeZone.getTimeZone("UTC"))
+ def nextDate(): Date = nextTime().toDate(java.util.TimeZone.getTimeZone("UTC"))
def nextBigDecimal(multiplier: Double = 1000000.00, precision: Int = 2): BigDecimal =
BigDecimal(multiplier * nextDouble, new MathContext(precision))
diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala
index 039f650..a10ab5f 100644
--- a/src/main/scala/xyz/driver/core/json.scala
+++ b/src/main/scala/xyz/driver/core/json.scala
@@ -96,6 +96,15 @@ object json {
}
}
+ implicit val base64Format = new RootJsonFormat[Base64] {
+ def write(base64Value: Base64) = JsString(base64Value.value)
+
+ def read(value: JsValue): Base64 = value match {
+ case JsString(base64Value) => Base64(base64Value)
+ case _ => throw DeserializationException("Base64 format expects string")
+ }
+ }
+
class EnumJsonFormat[T](mapping: (String, T)*) extends RootJsonFormat[T] {
private val map = mapping.toMap
diff --git a/src/main/scala/xyz/driver/core/rest.scala b/src/main/scala/xyz/driver/core/rest.scala
index 437df3c..af23cd5 100644
--- a/src/main/scala/xyz/driver/core/rest.scala
+++ b/src/main/scala/xyz/driver/core/rest.scala
@@ -11,6 +11,7 @@ import com.github.swagger.akka.model._
import com.github.swagger.akka.{HasActorSystem, SwaggerHttpService}
import com.typesafe.config.Config
import io.swagger.models.Scheme
+import xyz.driver.core.auth._
import xyz.driver.core.logging.Logger
import xyz.driver.core.stats.Stats
import xyz.driver.core.time.TimeRange
@@ -27,8 +28,8 @@ object rest {
trackingId: String = generators.nextUuid().toString,
contextHeaders: Map[String, String] = Map.empty[String, String]) {
- def authToken: Option[Auth.AuthToken] =
- contextHeaders.get(Auth.AuthProvider.AuthenticationTokenHeader).map(Auth.AuthToken.apply)
+ def authToken: Option[AuthToken] =
+ contextHeaders.get(AuthProvider.AuthenticationTokenHeader).map(AuthToken.apply)
}
object ServiceRequestContext {
@@ -67,66 +68,53 @@ object rest {
}
}
- object Auth {
-
- trait Permission
-
- trait Role {
- val id: Id[Role]
- val name: Name[Role]
- val permissions: Set[Permission]
-
- def hasPermission(permission: Permission): Boolean = permissions.contains(permission)
+ object AuthProvider {
+ val AuthenticationTokenHeader = ServiceRequestContext.ContextHeaders.AuthenticationTokenHeader
+ val SetAuthenticationTokenHeader = "set-authorization"
}
- trait User {
- def id: Id[User]
- def roles: Set[Role]
- def permissions: Set[Permission] = roles.flatMap(_.permissions)
- }
+ trait AuthProvider[U <: User] {
- final case class BasicUser(id: Id[User], roles: Set[Role]) extends User
+ import akka.http.scaladsl.server._
+ import Directives._
- final case class AuthToken(value: String)
+ implicit val execution: ExecutionContext
- final case class PasswordHash(value: String)
+ /**
+ * Specific implementation on how to extract user from request context,
+ * can either need to do a network call to auth server or extract everything from self-contained token
+ *
+ * @param context set of request values which can be relevant to authenticate user
+ * @return authenticated user
+ */
+ protected def authenticatedUser(context: ServiceRequestContext): OptionT[Future, U]
- object AuthProvider {
- val AuthenticationTokenHeader = ServiceRequestContext.ContextHeaders.AuthenticationTokenHeader
- val SetAuthenticationTokenHeader = "set-authorization"
- }
+ protected def userHasPermission(user: U, permission: Permission): Future[Boolean]
+
+ def authorize(permissions: Permission*): Directive1[U] = {
+ ServiceRequestContext.serviceContext flatMap { ctx =>
- trait AuthProvider[U <: User] {
-
- import akka.http.scaladsl.server._
- import Directives._
-
- /**
- * Specific implementation on how to extract user from request context,
- * can either need to do a network call to auth server or extract everything from self-contained token
- *
- * @param context set of request values which can be relevant to authenticate user
- * @return authenticated user
- */
- protected def authenticatedUser(context: ServiceRequestContext): OptionT[Future, U]
-
- def authorize(permissions: Permission*): Directive1[U] = {
- ServiceRequestContext.serviceContext flatMap { ctx =>
- onComplete(authenticatedUser(ctx).run).flatMap {
- case Success(Some(user)) =>
- if (permissions.forall(user.permissions.contains)) provide(user)
- else {
- val challenge =
- HttpChallenges.basic(s"User does not have the required permissions: ${permissions.mkString(", ")}")
- reject(AuthenticationFailedRejection(CredentialsRejected, challenge))
- }
-
- case Success(None) =>
- reject(ValidationRejection(s"Wasn't able to find authenticated user for the token provided"))
-
- case Failure(t) =>
- reject(ValidationRejection(s"Wasn't able to verify token for authenticated user", Some(t)))
+ onComplete(authenticatedUser(ctx).run flatMap { userOption =>
+ userOption.traverse[Future, (U, Boolean)] { user =>
+ permissions
+ .toList
+ .traverse[Future, Boolean](userHasPermission(user, _))
+ .map(results => user -> results.forall(identity))
}
+ }).flatMap {
+ case Success(Some((user, authorizationResult))) =>
+ if (authorizationResult) provide(user)
+ else {
+ val challenge =
+ HttpChallenges.basic(s"User does not have the required permissions: ${permissions.mkString(", ")}")
+ reject(AuthenticationFailedRejection(CredentialsRejected, challenge))
+ }
+
+ case Success(None) =>
+ reject(ValidationRejection(s"Wasn't able to find authenticated user for the token provided"))
+
+ case Failure(t) =>
+ reject(ValidationRejection(s"Wasn't able to verify token for authenticated user", Some(t)))
}
}
}
diff --git a/src/test/scala/xyz/driver/core/AuthTest.scala b/src/test/scala/xyz/driver/core/AuthTest.scala
index 57f79ff..50c8291 100644
--- a/src/test/scala/xyz/driver/core/AuthTest.scala
+++ b/src/test/scala/xyz/driver/core/AuthTest.scala
@@ -1,13 +1,14 @@
package xyz.driver.core
-import akka.http.scaladsl.testkit.ScalatestRouteTest
-import akka.http.scaladsl.server._
-import Directives._
import akka.http.scaladsl.model.headers.{HttpChallenges, RawHeader}
import akka.http.scaladsl.server.AuthenticationFailedRejection.CredentialsRejected
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.server._
+import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalatest.mock.MockitoSugar
import org.scalatest.{FlatSpec, Matchers}
-import xyz.driver.core.rest.Auth._
+import xyz.driver.core.auth._
+import xyz.driver.core.rest.AuthProvider
import xyz.driver.core.rest.ServiceRequestContext
import scala.concurrent.Future
@@ -18,13 +19,16 @@ class AuthTest extends FlatSpec with Matchers with MockitoSugar with ScalatestRo
case object TestRoleAllowedPermission extends Permission
case object TestRoleNotAllowedPermission extends Permission
- case object TestRole extends Role {
- val id = Id("1")
- val name = Name("testRole")
- val permissions = Set[Permission](TestRoleAllowedPermission)
- }
+ val TestRole = Role(Id("1"), Name("testRole"))
val authStatusService: AuthProvider[User] = new AuthProvider[User] {
+
+ override implicit val execution = scala.concurrent.ExecutionContext.global
+
+ override protected def userHasPermission(user: User, permission: Permission): Future[Boolean] = {
+ Future.successful(permission === TestRoleAllowedPermission)
+ }
+
override def authenticatedUser(context: ServiceRequestContext): OptionT[Future, User] = OptionT.optionT[Future] {
if (context.contextHeaders.keySet.contains(AuthProvider.AuthenticationTokenHeader)) {
Future.successful(Some(BasicUser(Id[User]("1"), Set(TestRole))))