+doc example snippet for akka http java dsl: SecurityDirectives (#20717)
This commit is contained in:
parent
f246c56087
commit
cc22ed4560
13 changed files with 442 additions and 11 deletions
|
|
@ -0,0 +1,364 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
package docs.http.javadsl.server.directives;
|
||||||
|
|
||||||
|
import akka.http.javadsl.model.HttpRequest;
|
||||||
|
import akka.http.javadsl.model.StatusCodes;
|
||||||
|
import akka.http.javadsl.model.headers.BasicHttpCredentials;
|
||||||
|
import akka.http.javadsl.model.headers.HttpChallenge;
|
||||||
|
import akka.http.javadsl.model.headers.HttpCredentials;
|
||||||
|
import akka.http.javadsl.server.Route;
|
||||||
|
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||||
|
import akka.japi.JavaPartialFunction;
|
||||||
|
import org.junit.Test;
|
||||||
|
import scala.PartialFunction;
|
||||||
|
import scala.util.Either;
|
||||||
|
import scala.util.Left;
|
||||||
|
import scala.util.Right;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class SecurityDirectivesExamplesTest extends JUnitRouteTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateBasic() {
|
||||||
|
//#authenticateBasic
|
||||||
|
final Function<Optional<ProvidedCredentials>, Optional<String>> myUserPassAuthenticator =
|
||||||
|
credentials ->
|
||||||
|
credentials.filter(c -> c.verify("p4ssw0rd")).map(ProvidedCredentials::identifier);
|
||||||
|
|
||||||
|
final Route route = path("secured", () ->
|
||||||
|
authenticateBasic("secure site", myUserPassAuthenticator, userName ->
|
||||||
|
complete("The user is '" + userName + "'")
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured"))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The resource requires authentication, which was not supplied with the request")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
|
||||||
|
final HttpCredentials validCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
|
||||||
|
.assertEntity("The user is 'John'");
|
||||||
|
|
||||||
|
final HttpCredentials invalidCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The supplied authentication is invalid")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
//#authenticateBasic
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateBasicPF() {
|
||||||
|
//#authenticateBasicPF
|
||||||
|
final PartialFunction<Optional<ProvidedCredentials>, String> myUserPassAuthenticator =
|
||||||
|
new JavaPartialFunction<Optional<ProvidedCredentials>, String>() {
|
||||||
|
@Override
|
||||||
|
public String apply(Optional<ProvidedCredentials> opt, boolean isCheck) throws Exception {
|
||||||
|
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
|
||||||
|
if (isCheck) return null;
|
||||||
|
else return opt.get().identifier();
|
||||||
|
} else if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd-special")).isPresent()) {
|
||||||
|
if (isCheck) return null;
|
||||||
|
else return opt.get().identifier() + "-admin";
|
||||||
|
} else {
|
||||||
|
throw noMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Route route = path("secured", () ->
|
||||||
|
authenticateBasicPF("secure site", myUserPassAuthenticator, userName ->
|
||||||
|
complete("The user is '" + userName + "'")
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured"))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The resource requires authentication, which was not supplied with the request")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
|
||||||
|
final HttpCredentials validCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
|
||||||
|
.assertEntity("The user is 'John'");
|
||||||
|
|
||||||
|
final HttpCredentials validAdminCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd-special");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validAdminCredentials))
|
||||||
|
.assertEntity("The user is 'John-admin'");
|
||||||
|
|
||||||
|
final HttpCredentials invalidCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The supplied authentication is invalid")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
//#authenticateBasicPF
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateBasicPFAsync() {
|
||||||
|
//#authenticateBasicPFAsync
|
||||||
|
class User {
|
||||||
|
private final String id;
|
||||||
|
public User(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final PartialFunction<Optional<ProvidedCredentials>, CompletionStage<User>> myUserPassAuthenticator =
|
||||||
|
new JavaPartialFunction<Optional<ProvidedCredentials>,CompletionStage<User>>() {
|
||||||
|
@Override
|
||||||
|
public CompletionStage<User> apply(Optional<ProvidedCredentials> opt, boolean isCheck) throws Exception {
|
||||||
|
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
|
||||||
|
if (isCheck) return CompletableFuture.completedFuture(null);
|
||||||
|
else return CompletableFuture.completedFuture(new User(opt.get().identifier()));
|
||||||
|
} else {
|
||||||
|
throw noMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Route route = path("secured", () ->
|
||||||
|
authenticateBasicPFAsync("secure site", myUserPassAuthenticator, user ->
|
||||||
|
complete("The user is '" + user.getId() + "'"))
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured"))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The resource requires authentication, which was not supplied with the request")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
|
||||||
|
final HttpCredentials validCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
|
||||||
|
.assertEntity("The user is 'John'");
|
||||||
|
|
||||||
|
final HttpCredentials invalidCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The supplied authentication is invalid")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
//#authenticateBasicPFAsync
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateBasicAsync() {
|
||||||
|
//#authenticateBasicAsync
|
||||||
|
final Function<Optional<ProvidedCredentials>, CompletionStage<Optional<String>>> myUserPassAuthenticator = opt -> {
|
||||||
|
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
|
||||||
|
return CompletableFuture.completedFuture(Optional.of(opt.get().identifier()));
|
||||||
|
} else {
|
||||||
|
return CompletableFuture.completedFuture(Optional.empty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Route route = path("secured", () ->
|
||||||
|
authenticateBasicAsync("secure site", myUserPassAuthenticator, userName ->
|
||||||
|
complete("The user is '" + userName + "'")
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured"))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The resource requires authentication, which was not supplied with the request")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
|
||||||
|
final HttpCredentials validCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
|
||||||
|
.assertEntity("The user is 'John'");
|
||||||
|
|
||||||
|
final HttpCredentials invalidCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The supplied authentication is invalid")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
|
||||||
|
//#authenticateBasicAsync
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateOrRejectWithChallenge() {
|
||||||
|
//#authenticateOrRejectWithChallenge
|
||||||
|
final HttpChallenge challenge = HttpChallenge.create("MyAuth", "MyRealm");
|
||||||
|
|
||||||
|
// your custom authentication logic:
|
||||||
|
final Function<HttpCredentials, Boolean> auth = credentials -> true;
|
||||||
|
|
||||||
|
final Function<Optional<HttpCredentials>, CompletionStage<Either<HttpChallenge, String>>> myUserPassAuthenticator =
|
||||||
|
opt -> {
|
||||||
|
if (opt.isPresent() && auth.apply(opt.get())) {
|
||||||
|
return CompletableFuture.completedFuture(Right.apply("some-user-name-from-creds"));
|
||||||
|
} else {
|
||||||
|
return CompletableFuture.completedFuture(Left.apply(challenge));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Route route = path("secured", () ->
|
||||||
|
authenticateOrRejectWithChallenge(myUserPassAuthenticator, userName ->
|
||||||
|
complete("Authenticated!")
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured"))
|
||||||
|
.assertStatusCode(StatusCodes.UNAUTHORIZED)
|
||||||
|
.assertEntity("The resource requires authentication, which was not supplied with the request")
|
||||||
|
.assertHeaderExists("WWW-Authenticate", "MyAuth realm=\"MyRealm\"");
|
||||||
|
|
||||||
|
final HttpCredentials validCredentials =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
|
||||||
|
.assertStatusCode(StatusCodes.OK)
|
||||||
|
.assertEntity("Authenticated!");
|
||||||
|
//#authenticateOrRejectWithChallenge
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthorize() {
|
||||||
|
//#authorize
|
||||||
|
class User {
|
||||||
|
private final String name;
|
||||||
|
public User(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticate the user:
|
||||||
|
final Function<Optional<ProvidedCredentials>, Optional<User>> myUserPassAuthenticator =
|
||||||
|
opt -> {
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
return Optional.of(new User(opt.get().identifier()));
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if user is authorized to perform admin actions:
|
||||||
|
final Set<String> admins = new HashSet<>();
|
||||||
|
admins.add("Peter");
|
||||||
|
final Function<User, Boolean> hasAdminPermissions = user -> admins.contains(user.getName());
|
||||||
|
|
||||||
|
final Route route = authenticateBasic("secure site", myUserPassAuthenticator, user ->
|
||||||
|
path("peters-lair", () ->
|
||||||
|
authorize(() -> hasAdminPermissions.apply(user), () ->
|
||||||
|
complete("'" + user.getName() +"' visited Peter's lair")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
final HttpCredentials johnsCred =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(johnsCred))
|
||||||
|
.assertStatusCode(StatusCodes.FORBIDDEN)
|
||||||
|
.assertEntity("The supplied authentication is not authorized to access this resource");
|
||||||
|
|
||||||
|
final HttpCredentials petersCred =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(petersCred))
|
||||||
|
.assertEntity("'Peter' visited Peter's lair");
|
||||||
|
//#authorize
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthorizeAsync() {
|
||||||
|
//#authorizeAsync
|
||||||
|
class User {
|
||||||
|
private final String name;
|
||||||
|
public User(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticate the user:
|
||||||
|
final Function<Optional<ProvidedCredentials>, Optional<User>> myUserPassAuthenticator =
|
||||||
|
opt -> {
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
return Optional.of(new User(opt.get().identifier()));
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if user is authorized to perform admin actions,
|
||||||
|
// this could potentially be a long operation so it would return a Future
|
||||||
|
final Set<String> admins = new HashSet<>();
|
||||||
|
admins.add("Peter");
|
||||||
|
final Set<String> synchronizedAdmins = Collections.synchronizedSet(admins);
|
||||||
|
|
||||||
|
final Function<User, CompletionStage<Object>> hasAdminPermissions =
|
||||||
|
user -> CompletableFuture.completedFuture(synchronizedAdmins.contains(user.getName()));
|
||||||
|
|
||||||
|
final Route route = authenticateBasic("secure site", myUserPassAuthenticator, user ->
|
||||||
|
path("peters-lair", () ->
|
||||||
|
authorizeAsync(() -> hasAdminPermissions.apply(user), () ->
|
||||||
|
complete("'" + user.getName() +"' visited Peter's lair")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).seal(system(), materializer());
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
final HttpCredentials johnsCred =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(johnsCred))
|
||||||
|
.assertStatusCode(StatusCodes.FORBIDDEN)
|
||||||
|
.assertEntity("The supplied authentication is not authorized to access this resource");
|
||||||
|
|
||||||
|
final HttpCredentials petersCred =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(petersCred))
|
||||||
|
.assertEntity("'Peter' visited Peter's lair");
|
||||||
|
//#authorizeAsync
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtractCredentials() {
|
||||||
|
//#extractCredentials
|
||||||
|
final Route route = extractCredentials(optCreds -> {
|
||||||
|
if (optCreds.isPresent()) {
|
||||||
|
return complete("Credentials: " + optCreds.get());
|
||||||
|
} else {
|
||||||
|
return complete("No credentials");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
final HttpCredentials johnsCred =
|
||||||
|
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
|
||||||
|
testRoute(route).run(HttpRequest.GET("/").addCredentials(johnsCred))
|
||||||
|
.assertEntity("Credentials: Basic Sm9objpwNHNzdzByZA==");
|
||||||
|
|
||||||
|
testRoute(route).run(HttpRequest.GET("/"))
|
||||||
|
.assertEntity("No credentials");
|
||||||
|
//#extractCredentials
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,4 +27,5 @@ See :ref:`credentials-and-timing-attacks-java` for details about verifying the s
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasic
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,5 @@ See :ref:`credentials-and-timing-attacks-java` for details about verifying the s
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicAsync
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,5 @@ See :ref:`credentials-and-timing-attacks-java` for details about verifying the s
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicPF
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,5 @@ See :ref:`credentials-and-timing-attacks-java` for details about verifying the s
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicPFAsync
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,5 @@ More details about challenge-response authentication are available in the `RFC 2
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateOrRejectWithChallenge
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,5 @@ See also :ref:`-authorize-java-` for the asynchronous version of this directive.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authorize
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,5 @@ See also :ref:`-authorize-java-` for the synchronous version of this directive.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authorizeAsync
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,5 @@ See :ref:`credentials-and-timing-attacks-java` for details about verifying the s
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
|
||||||
|
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#extractCredentials
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package akka.http.javadsl.model;
|
||||||
|
|
||||||
import akka.Done;
|
import akka.Done;
|
||||||
import akka.stream.Materializer;
|
import akka.stream.Materializer;
|
||||||
|
import akka.http.javadsl.model.headers.HttpCredentials;
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
import scala.concurrent.Future;
|
import scala.concurrent.Future;
|
||||||
|
|
||||||
|
|
@ -113,6 +114,11 @@ public interface HttpMessage {
|
||||||
*/
|
*/
|
||||||
Self addHeaders(Iterable<HttpHeader> headers);
|
Self addHeaders(Iterable<HttpHeader> headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this message with the given http credential header added to the list of headers.
|
||||||
|
*/
|
||||||
|
Self addCredentials(HttpCredentials credentials);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of this message with all headers of the given name (case-insensitively) removed.
|
* Returns a copy of this message with all headers of the given name (case-insensitively) removed.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ sealed trait HttpMessage extends jm.HttpMessage {
|
||||||
|
|
||||||
def addHeader(header: jm.HttpHeader): Self = mapHeaders(_ :+ header.asInstanceOf[HttpHeader])
|
def addHeader(header: jm.HttpHeader): Self = mapHeaders(_ :+ header.asInstanceOf[HttpHeader])
|
||||||
|
|
||||||
|
def addCredentials(credentials: jm.headers.HttpCredentials): Self = addHeader(jm.headers.Authorization.create(credentials))
|
||||||
|
|
||||||
/** Removes the header with the given name (case-insensitive) */
|
/** Removes the header with the given name (case-insensitive) */
|
||||||
def removeHeader(headerName: String): Self = {
|
def removeHeader(headerName: String): Self = {
|
||||||
val lowerHeaderName = headerName.toRootLowerCase
|
val lowerHeaderName = headerName.toRootLowerCase
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,11 @@ import scala.compat.java8.FutureConverters._
|
||||||
import scala.compat.java8.OptionConverters._
|
import scala.compat.java8.OptionConverters._
|
||||||
import akka.http.javadsl.model.headers.HttpChallenge
|
import akka.http.javadsl.model.headers.HttpChallenge
|
||||||
import akka.http.javadsl.model.headers.HttpCredentials
|
import akka.http.javadsl.model.headers.HttpCredentials
|
||||||
import akka.http.javadsl.server.{ RequestContext, Route }
|
import akka.http.javadsl.server.{ Route, RequestContext }
|
||||||
import akka.http.scaladsl
|
import akka.http.scaladsl
|
||||||
import akka.http.scaladsl.server.{ AuthorizationFailedRejection, Directives ⇒ D }
|
import akka.http.scaladsl.server.{ Directives ⇒ D }
|
||||||
|
|
||||||
|
import scala.concurrent.{ ExecutionContextExecutor, Future }
|
||||||
|
|
||||||
object SecurityDirectives {
|
object SecurityDirectives {
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,6 +70,50 @@ abstract class SecurityDirectives extends SchemeDirectives {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the inner route with Http Basic authentication support.
|
||||||
|
* The given authenticator determines whether the credentials in the request are valid
|
||||||
|
* and, if so, which user object to supply to the inner route.
|
||||||
|
*
|
||||||
|
* Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty.
|
||||||
|
*/
|
||||||
|
def authenticateBasicPF[T](realm: String, authenticator: PartialFunction[Optional[ProvidedCredentials], T],
|
||||||
|
inner: JFunction[T, Route]): Route = RouteAdapter {
|
||||||
|
def pf: PartialFunction[scaladsl.server.directives.Credentials, Option[T]] = {
|
||||||
|
case c ⇒ Option(authenticator.applyOrElse(toJava(c), (_: Any) ⇒ null.asInstanceOf[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
D.authenticateBasic(realm, pf) { t ⇒
|
||||||
|
inner.apply(t).delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the inner route with Http Basic authentication support.
|
||||||
|
* The given authenticator determines whether the credentials in the request are valid
|
||||||
|
* and, if so, which user object to supply to the inner route.
|
||||||
|
*
|
||||||
|
* Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty.
|
||||||
|
*/
|
||||||
|
def authenticateBasicPFAsync[T](realm: String, authenticator: PartialFunction[Optional[ProvidedCredentials], CompletionStage[T]],
|
||||||
|
inner: JFunction[T, Route]): Route = RouteAdapter {
|
||||||
|
def pf(implicit ec: ExecutionContextExecutor): PartialFunction[scaladsl.server.directives.Credentials, Future[Option[T]]] = {
|
||||||
|
case credentials ⇒
|
||||||
|
val jCredentials = toJava(credentials)
|
||||||
|
if (authenticator isDefinedAt jCredentials) {
|
||||||
|
authenticator(jCredentials).toScala.map(Some(_))
|
||||||
|
} else {
|
||||||
|
Future.successful(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
D.extractExecutionContext { implicit ec ⇒
|
||||||
|
D.authenticateBasicAsync(realm, pf) { t ⇒
|
||||||
|
inner.apply(t).delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`.
|
* Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`.
|
||||||
* The given authenticator determines whether the credentials in the request are valid
|
* The given authenticator determines whether the credentials in the request are valid
|
||||||
|
|
@ -261,4 +307,4 @@ abstract class SecurityDirectives extends SchemeDirectives {
|
||||||
*/
|
*/
|
||||||
def challengeFor(realm: String): HttpChallenge = HttpChallenge.create("Basic", realm)
|
def challengeFor(realm: String): HttpChallenge = HttpChallenge.create("Basic", realm)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -886,6 +886,11 @@ object MiMa extends AutoPlugin {
|
||||||
|
|
||||||
// #20288 migrate BodyPartRenderer to GraphStage
|
// #20288 migrate BodyPartRenderer to GraphStage
|
||||||
ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.http.impl.engine.rendering.BodyPartRenderer.streamed")
|
ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.http.impl.engine.rendering.BodyPartRenderer.streamed")
|
||||||
|
),
|
||||||
|
"2.4.8" -> Seq(
|
||||||
|
// #20717 example snippet for akka http java dsl: SecurityDirectives
|
||||||
|
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.javadsl.model.HttpMessage#MessageTransformations.addCredentials"),
|
||||||
|
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.HttpMessage.addCredentials")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue