aboutsummaryrefslogtreecommitdiff
path: root/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala
diff options
context:
space:
mode:
Diffstat (limited to 'core-rest/src/test/scala/xyz/driver/core/AuthTest.scala')
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/AuthTest.scala165
1 files changed, 165 insertions, 0 deletions
diff --git a/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala b/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala
new file mode 100644
index 0000000..2e772fb
--- /dev/null
+++ b/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala
@@ -0,0 +1,165 @@
+package xyz.driver.core
+
+import akka.http.scaladsl.model.headers.{
+ HttpChallenges,
+ OAuth2BearerToken,
+ RawHeader,
+ Authorization => AkkaAuthorization
+}
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.server._
+import akka.http.scaladsl.testkit.ScalatestRouteTest
+import org.scalatest.{FlatSpec, Matchers}
+import pdi.jwt.{Jwt, JwtAlgorithm}
+import xyz.driver.core.auth._
+import xyz.driver.core.domain.Email
+import xyz.driver.core.logging._
+import xyz.driver.core.rest._
+import xyz.driver.core.rest.auth._
+import xyz.driver.core.time.Time
+
+import scala.concurrent.Future
+import scalaz.OptionT
+
+class AuthTest extends FlatSpec with Matchers with ScalatestRouteTest {
+
+ case object TestRoleAllowedPermission extends Permission
+ case object TestRoleAllowedByTokenPermission extends Permission
+ case object TestRoleNotAllowedPermission extends Permission
+
+ val TestRole = Role(Id("1"), Name("testRole"))
+
+ val (publicKey, privateKey) = {
+ import java.security.KeyPairGenerator
+
+ val keygen = KeyPairGenerator.getInstance("RSA")
+ keygen.initialize(2048)
+
+ val keyPair = keygen.generateKeyPair()
+ (keyPair.getPublic, keyPair.getPrivate)
+ }
+
+ val basicAuthorization: Authorization[User] = new Authorization[User] {
+
+ override def userHasPermissions(user: User, permissions: Seq[Permission])(
+ implicit ctx: ServiceRequestContext): Future[AuthorizationResult] = {
+ val authorized = permissions.map(p => p -> (p === TestRoleAllowedPermission)).toMap
+ Future.successful(AuthorizationResult(authorized, ctx.permissionsToken))
+ }
+ }
+
+ val tokenIssuer = "users"
+ val tokenAuthorization = new CachedTokenAuthorization[User](publicKey, tokenIssuer)
+
+ val authorization = new ChainedAuthorization[User](tokenAuthorization, basicAuthorization)
+
+ val authStatusService = new AuthProvider[User](authorization, NoLogger) {
+ override def authenticatedUser(implicit ctx: ServiceRequestContext): OptionT[Future, User] =
+ OptionT.optionT[Future] {
+ if (ctx.contextHeaders.keySet.contains(AuthProvider.AuthenticationTokenHeader)) {
+ Future.successful(
+ Some(
+ AuthTokenUserInfo(
+ Id[User]("1"),
+ Email("foo", "bar"),
+ emailVerified = true,
+ audience = "driver",
+ roles = Set(TestRole),
+ expirationTime = Time(1000000L)
+ )))
+ } else {
+ Future.successful(Option.empty[User])
+ }
+ }
+ }
+
+ import authStatusService._
+
+ "'authorize' directive" should "throw error if auth token is not in the request" in {
+
+ Get("/naive/attempt") ~>
+ authorize(TestRoleAllowedPermission) { user =>
+ complete("Never going to be here")
+ } ~>
+ check {
+ // handled shouldBe false
+ rejections should contain(
+ AuthenticationFailedRejection(
+ AuthenticationFailedRejection.CredentialsMissing,
+ HttpChallenges.oAuth2(authStatusService.realm)))
+ }
+ }
+
+ it should "throw error if authorized user does not have the requested permission" in {
+
+ val referenceAuthToken = AuthToken("I am a test role's token")
+ val referenceAuthHeader = AkkaAuthorization(OAuth2BearerToken(referenceAuthToken.value))
+
+ Post("/administration/attempt").addHeader(
+ referenceAuthHeader
+ ) ~>
+ authorize(TestRoleNotAllowedPermission) { user =>
+ complete("Never going to get here")
+ } ~>
+ check {
+ handled shouldBe false
+ rejections should contain(AuthorizationFailedRejection)
+ }
+ }
+
+ it should "pass and retrieve the token to client code, if token is in request and user has permission" in {
+ val referenceAuthToken = AuthToken("I am token")
+ val referenceAuthHeader = AkkaAuthorization(OAuth2BearerToken(referenceAuthToken.value))
+
+ Get("/valid/attempt/?a=2&b=5").addHeader(
+ referenceAuthHeader
+ ) ~>
+ authorize(TestRoleAllowedPermission) { ctx =>
+ complete(s"Alright, user ${ctx.authenticatedUser.id} is authorized")
+ } ~>
+ check {
+ handled shouldBe true
+ responseAs[String] shouldBe "Alright, user 1 is authorized"
+ }
+ }
+
+ it should "authenticate correctly even without the 'Bearer' prefix on the Authorization header" in {
+ val referenceAuthToken = AuthToken("unprefixed_token")
+
+ Get("/valid/attempt/?a=2&b=5").addHeader(
+ RawHeader(ContextHeaders.AuthenticationTokenHeader, referenceAuthToken.value)
+ ) ~>
+ authorize(TestRoleAllowedPermission) { ctx =>
+ complete(s"Alright, user ${ctx.authenticatedUser.id} is authorized")
+ } ~>
+ check {
+ handled shouldBe true
+ responseAs[String] shouldBe "Alright, user 1 is authorized"
+ }
+ }
+
+ it should "authorize permission found in permissions token" in {
+ import spray.json._
+
+ val claim = JsObject(
+ Map(
+ "iss" -> JsString(tokenIssuer),
+ "sub" -> JsString("1"),
+ "permissions" -> JsObject(Map(TestRoleAllowedByTokenPermission.toString -> JsBoolean(true)))
+ )).prettyPrint
+ val permissionsToken = PermissionsToken(Jwt.encode(claim, privateKey, JwtAlgorithm.RS256))
+ val referenceAuthToken = AuthToken("I am token")
+ val referenceAuthHeader = AkkaAuthorization(OAuth2BearerToken(referenceAuthToken.value))
+
+ Get("/alic/attempt/?a=2&b=5")
+ .addHeader(referenceAuthHeader)
+ .addHeader(RawHeader(AuthProvider.PermissionsTokenHeader, permissionsToken.value)) ~>
+ authorize(TestRoleAllowedByTokenPermission) { ctx =>
+ complete(s"Alright, user ${ctx.authenticatedUser.id} is authorized by permissions token")
+ } ~>
+ check {
+ handled shouldBe true
+ responseAs[String] shouldBe "Alright, user 1 is authorized by permissions token"
+ }
+ }
+}