This commit is contained in:
parent
d4d9c1943e
commit
dce174b455
5 changed files with 33 additions and 28 deletions
|
|
@ -20,4 +20,12 @@ public abstract class HttpChallenge {
|
|||
public static HttpChallenge create(String scheme, String realm, Map<String, String> params) {
|
||||
return new akka.http.scaladsl.model.headers.HttpChallenge(scheme, realm, Util.convertMapToScala(params));
|
||||
}
|
||||
}
|
||||
|
||||
public static HttpChallenge createBasic(String realm) {
|
||||
return create("Basic", realm);
|
||||
}
|
||||
|
||||
public static HttpChallenge createOAuth2(String realm) {
|
||||
return create("Bearer", realm);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,3 +21,10 @@ final case class HttpChallenge(scheme: String, realm: String,
|
|||
/** Java API */
|
||||
def getParams: util.Map[String, String] = params.asJava
|
||||
}
|
||||
|
||||
object HttpChallenges {
|
||||
|
||||
def basic(realm: String): HttpChallenge = HttpChallenge("Basic", realm)
|
||||
|
||||
def oAuth2(realm: String): HttpChallenge = HttpChallenge("Bearer", realm)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,18 +18,19 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
val doOAuth2Auth = authenticateOAuth2PF("MyRealm", { case Credentials.Provided(identifier) ⇒ identifier })
|
||||
val authWithAnonymous = doBasicAuth.withAnonymousUser("We are Legion")
|
||||
|
||||
val challenge = HttpChallenge("Basic", "MyRealm")
|
||||
val basicChallenge = HttpChallenges.basic("MyRealm")
|
||||
val oAuth2Challenge = HttpChallenges.oAuth2("MyRealm")
|
||||
|
||||
"basic authentication" should {
|
||||
"reject requests without Authorization header with an AuthenticationFailedRejection" in {
|
||||
Get() ~> {
|
||||
dontBasicAuth { echoComplete }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, challenge) }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, basicChallenge) }
|
||||
}
|
||||
"reject unauthenticated requests with Authorization header with an AuthenticationFailedRejection" in {
|
||||
Get() ~> Authorization(BasicHttpCredentials("Bob", "")) ~> {
|
||||
dontBasicAuth { echoComplete }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, challenge) }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, basicChallenge) }
|
||||
}
|
||||
"reject requests with an OAuth2 Bearer Token Authorization header with 401" in {
|
||||
Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> Route.seal {
|
||||
|
|
@ -37,7 +38,7 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
} ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The supplied authentication is invalid"
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(challenge))
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(oAuth2Challenge))
|
||||
}
|
||||
}
|
||||
"reject requests with illegal Authorization header with 401" in {
|
||||
|
|
@ -46,7 +47,7 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
} ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(challenge))
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(basicChallenge))
|
||||
}
|
||||
}
|
||||
"extract the object representing the user identity created by successful authentication" in {
|
||||
|
|
@ -74,12 +75,12 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
"reject requests without Authorization header with an AuthenticationFailedRejection" in {
|
||||
Get() ~> {
|
||||
dontOAuth2Auth { echoComplete }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, challenge) }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, oAuth2Challenge) }
|
||||
}
|
||||
"reject unauthenticated requests with Authorization header with an AuthenticationFailedRejection" in {
|
||||
Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> {
|
||||
dontOAuth2Auth { echoComplete }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, challenge) }
|
||||
} ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, oAuth2Challenge) }
|
||||
}
|
||||
"reject requests with a Basic Authorization header with 401" in {
|
||||
Get() ~> Authorization(BasicHttpCredentials("Alice", "")) ~> Route.seal {
|
||||
|
|
@ -87,7 +88,7 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
} ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The supplied authentication is invalid"
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(challenge))
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(basicChallenge))
|
||||
}
|
||||
}
|
||||
"reject requests with illegal Authorization header with 401" in {
|
||||
|
|
@ -96,7 +97,7 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
} ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(challenge))
|
||||
header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(oAuth2Challenge))
|
||||
}
|
||||
}
|
||||
"extract the object representing the user identity created by successful authentication" in {
|
||||
|
|
@ -132,7 +133,7 @@ class SecurityDirectivesSpec extends RoutingSpec {
|
|||
status shouldEqual StatusCodes.Unauthorized
|
||||
headers.collect {
|
||||
case `WWW-Authenticate`(challenge +: Nil) ⇒ challenge
|
||||
} shouldEqual Seq(challenge, otherChallenge)
|
||||
} shouldEqual Seq(basicChallenge, otherChallenge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,10 +301,4 @@ abstract class SecurityDirectives extends SchemeDirectives {
|
|||
def authorizeAsyncWithRequestContext(check: akka.japi.function.Function[RequestContext, CompletionStage[Boolean]], inner: Supplier[Route]): Route = RouteAdapter {
|
||||
D.authorizeAsync(rc ⇒ check(RequestContext.wrap(rc)).toScala)(inner.get().delegate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a `Basic` [[HttpChallenge]] for the given realm.
|
||||
*/
|
||||
def challengeFor(realm: String): HttpChallenge = HttpChallenge.create("Basic", realm)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,15 @@ import akka.http.scaladsl.util.FastFuture._
|
|||
import akka.http.scaladsl.model.headers._
|
||||
import akka.http.scaladsl.server.AuthenticationFailedRejection.{ CredentialsRejected, CredentialsMissing }
|
||||
|
||||
import scala.util.{ Try, Success }
|
||||
import scala.util.Success
|
||||
|
||||
/**
|
||||
* Provides directives for securing an inner route using the standard Http authentication headers [[`WWW-Authenticate`]]
|
||||
* and [[Authorization]]. Most prominently, HTTP Basic authentication as defined in RFC 2617.
|
||||
* and [[Authorization]]. Most prominently, HTTP Basic authentication and OAuth 2.0 Authorization Framework
|
||||
* as defined in RFC 2617 and RFC 6750 respectively.
|
||||
*
|
||||
* See: <a href="https://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a>.
|
||||
* See: <a href="https://www.ietf.org/rfc/rfc6750.txt">RFC 6750</a>.
|
||||
*
|
||||
* @groupname security Security directives
|
||||
* @groupprio security 220
|
||||
|
|
@ -95,7 +97,7 @@ trait SecurityDirectives {
|
|||
authenticateOrRejectWithChallenge[BasicHttpCredentials, T] { cred ⇒
|
||||
authenticator(Credentials(cred)).fast.map {
|
||||
case Some(t) ⇒ AuthenticationResult.success(t)
|
||||
case None ⇒ AuthenticationResult.failWithChallenge(challengeFor(realm))
|
||||
case None ⇒ AuthenticationResult.failWithChallenge(HttpChallenges.basic(realm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -146,7 +148,7 @@ trait SecurityDirectives {
|
|||
authenticateOrRejectWithChallenge[OAuth2BearerToken, T] { cred ⇒
|
||||
authenticator(Credentials(cred)).fast.map {
|
||||
case Some(t) ⇒ AuthenticationResult.success(t)
|
||||
case None ⇒ AuthenticationResult.failWithChallenge(challengeFor(realm))
|
||||
case None ⇒ AuthenticationResult.failWithChallenge(HttpChallenges.oAuth2(realm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -248,13 +250,6 @@ trait SecurityDirectives {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a `Basic` [[HttpChallenge]] for the given realm.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def challengeFor(realm: String) = HttpChallenge(scheme = "Basic", realm = realm, params = Map.empty)
|
||||
}
|
||||
|
||||
object SecurityDirectives extends SecurityDirectives
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue