From 08a9da58c1dfced2499d0068c71c85561d1cea86 Mon Sep 17 00:00:00 2001 From: Besmir Beqiri Date: Thu, 14 Dec 2023 15:25:18 +0100 Subject: [PATCH] Add `jpro-auth-routing` module --- build.gradle | 8 +- jpro-auth/core/build.gradle | 4 +- jpro-auth/core/src/main/java/module-info.java | 10 +- jpro-auth/routing/build.gradle | 29 ++++ .../routing/src/main/java/module-info.java | 11 ++ .../platform/auth/routing/AuthFilters.java | 145 ++++++++++++++++++ settings.gradle | 1 + 7 files changed, 198 insertions(+), 10 deletions(-) create mode 100755 jpro-auth/routing/build.gradle create mode 100644 jpro-auth/routing/src/main/java/module-info.java create mode 100644 jpro-auth/routing/src/main/java/one/jpro/platform/auth/routing/AuthFilters.java diff --git a/build.gradle b/build.gradle index 8090059c..35e518db 100644 --- a/build.gradle +++ b/build.gradle @@ -61,10 +61,10 @@ configure(subprojects.findAll { it.name != 'example' }) { } } -configure([project("tree-showing"), project("jpro-auth:core"), project("jpro-file"), project("jpro-image-manager"), - project("jpro-mdfx"), project("jpro-media"), project("jpro-sessions"), project("jpro-html-scrollpane"), - project("freeze-detector"), project("jpro-routing:core"), project("jpro-routing:dev"), - project("jpro-routing:popup"), project("jpro-webrtc"), project("jpro-youtube"), +configure([project("tree-showing"), project("jpro-auth:core"), project("jpro-auth:routing"), project("jpro-file"), + project("jpro-image-manager"), project("jpro-mdfx"), project("jpro-media"), project("jpro-sessions"), + project("jpro-html-scrollpane"), project("freeze-detector"), project("jpro-routing:core"), + project("jpro-routing:dev"), project("jpro-routing:popup"), project("jpro-webrtc"), project("jpro-youtube"), project("internal:openlink")]) { apply plugin: 'maven-publish' diff --git a/jpro-auth/core/build.gradle b/jpro-auth/core/build.gradle index 4f29d3fe..57153c85 100755 --- a/jpro-auth/core/build.gradle +++ b/jpro-auth/core/build.gradle @@ -18,8 +18,8 @@ publishing { mavenJava(MavenPublication) { pom { packaging = 'jar' - name = 'JPro Auth' - description = 'A library for authenticating and authorizing users in JavaFX applications running via JPro server.' + name = 'JPro Auth Core' + description = 'Core library for authenticating and authorizing users.' url = 'https://www.jpro.one' developers { diff --git a/jpro-auth/core/src/main/java/module-info.java b/jpro-auth/core/src/main/java/module-info.java index ffcb8969..a53fbe73 100644 --- a/jpro-auth/core/src/main/java/module-info.java +++ b/jpro-auth/core/src/main/java/module-info.java @@ -3,18 +3,20 @@ * * @author Besmir Beqiri */ -module one.jpro.platform.auth { +module one.jpro.platform.auth.core { requires javafx.graphics; + requires org.jetbrains.annotations; requires java.net.http; requires jpro.webapi; - requires org.jetbrains.annotations; - requires org.json; requires jwks.rsa; - requires org.slf4j; requires com.auth0.jwt; requires one.jpro.platform.internal.openlink; + requires transitive org.json; + requires transitive org.slf4j; + opens one.jpro.platform.auth.core; + exports one.jpro.platform.auth.core; exports one.jpro.platform.auth.core.api; exports one.jpro.platform.auth.core.authentication; diff --git a/jpro-auth/routing/build.gradle b/jpro-auth/routing/build.gradle new file mode 100755 index 00000000..6f0aca7e --- /dev/null +++ b/jpro-auth/routing/build.gradle @@ -0,0 +1,29 @@ +dependencies { + api project(":jpro-auth:core") + api project(":jpro-routing:core") + + testImplementation "org.junit.jupiter:junit-jupiter-api:$JUNIT_VERSION" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$JUNIT_VERSION" + testRuntimeOnly "ch.qos.logback:logback-classic:$LOGBACK_VERSION" +} + +publishing { + publications { + mavenJava(MavenPublication) { + pom { + packaging = 'jar' + name = 'JPro Auth Routing' + description = 'A library that makes it easy to combine and use the auth and routing functionalities.' + url = 'https://www.jpro.one' + + developers { + developer { + id = 'besidev' + name = 'Besmir Beqiri' + email = 'bb@sandec.de' + } + } + } + } + } +} diff --git a/jpro-auth/routing/src/main/java/module-info.java b/jpro-auth/routing/src/main/java/module-info.java new file mode 100644 index 00000000..3993a1ba --- /dev/null +++ b/jpro-auth/routing/src/main/java/module-info.java @@ -0,0 +1,11 @@ +/** + * Module descriptor for the Auth Routing module. + * + * @author Besmir Beqiri + */ +module one.jpro.platform.auth.routing { + requires transitive one.jpro.platform.auth.core; + requires transitive one.jpro.platform.routing.core; + + exports one.jpro.platform.auth.routing; +} \ No newline at end of file diff --git a/jpro-auth/routing/src/main/java/one/jpro/platform/auth/routing/AuthFilters.java b/jpro-auth/routing/src/main/java/one/jpro/platform/auth/routing/AuthFilters.java new file mode 100644 index 00000000..e8d2f084 --- /dev/null +++ b/jpro-auth/routing/src/main/java/one/jpro/platform/auth/routing/AuthFilters.java @@ -0,0 +1,145 @@ +package one.jpro.platform.auth.routing; + +import one.jpro.platform.auth.core.authentication.User; +import one.jpro.platform.auth.core.jwt.JWTAuthenticationProvider; +import one.jpro.platform.auth.core.oauth2.OAuth2AuthenticationProvider; +import one.jpro.platform.auth.core.oauth2.OAuth2Credentials; +import one.jpro.platform.routing.Filter; +import one.jpro.platform.routing.Response; +import one.jpro.platform.routing.Route; +import org.json.JSONObject; +import simplefx.experimental.parts.FXFuture; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Utility class with authorization filters used in the routing process. + * + * @author Besmir Beqiri + */ +public final class AuthFilters { + + /** + * Creates {@link Route} filter from a given {@link OAuth2AuthenticationProvider}, + * {@link OAuth2Credentials} and an operation a given user if the authentication + * is successful. + * + * @param authProvider the JWT authentication provider + * @param credentials a JSON object with the authentication information + * @param authPath the authentication path for the routing + * @param tokenPath the token path + * @param userConsumer operation on the given user argument + * @param errorConsumer operation on the given error argument + * @return a {@link Filter} object + */ + static Filter jwt(JWTAuthenticationProvider authProvider, + JSONObject credentials, + String authPath, + String tokenPath, + Consumer userConsumer, + Consumer errorConsumer) { + Objects.requireNonNull(authProvider, "auth provider cannot be null"); + Objects.requireNonNull(credentials, "credentials cannot be null"); + Objects.requireNonNull(authPath, "authentication path cannot be null"); + Objects.requireNonNull(tokenPath, "token path cannot be null"); + Objects.requireNonNull(userConsumer, "user consumer cannot be null"); + Objects.requireNonNull(errorConsumer, "error consumer cannot be null"); + + return (route) -> (request) -> { + if (request.path().equals(authPath)) { + return FXFuture.fromJava(authProvider.token(tokenPath, credentials) + .thenCompose(authProvider::authenticate)) + .flatMap(user -> { + userConsumer.accept(user); + return route.apply(request); + }) + .flatExceptionally(ex -> { + errorConsumer.accept(ex); + return route.apply(request); + }); + } else { + return route.apply(request); + } + }; + } + + /** + * Creates {@link Route} filter from a given {@link OAuth2AuthenticationProvider}, + * {@link OAuth2Credentials} and an operation a given user if the authentication + * is successful. + * + * @param authProvider the OAuth2 authentication provider + * @param credentials the OAuth2 credentials + * @param userConsumer consumer operation on the given user argument + * @param errorConsumer consumer operation on the given error argument + * @return a {@link Filter} object + */ + public static Filter oauth2(OAuth2AuthenticationProvider authProvider, + OAuth2Credentials credentials, + Consumer userConsumer, + Consumer errorConsumer) { + Objects.requireNonNull(authProvider, "auth provider can not be null"); + Objects.requireNonNull(credentials, "credentials can not be null"); + Objects.requireNonNull(userConsumer, "user consumer can not be null"); + Objects.requireNonNull(errorConsumer, "error consumer cannot be null"); + + return (route) -> (request) -> { + if (request.path().equals(credentials.getRedirectUri())) { + return FXFuture.fromJava(authProvider.authenticate(credentials)) + .flatMap(user -> { + userConsumer.accept(user); + return route.apply(request); + }) + .flatExceptionally(ex -> { + errorConsumer.accept(ex); + return route.apply(request); + }); + } else { + return route.apply(request); + } + }; + } + + /** + * Creates {@link Route} filter from a given {@link OAuth2AuthenticationProvider}, + * {@link OAuth2Credentials} and an operation a given user if the authentication + * is successful. + * + * @param authProvider the OAuth2 authentication provider + * @param credentials the OAuth2 credentials + * @param userFunction function operation on the given user argument + * @param errorFunction function operation on the given error argument + * @return a {@link Filter} object + */ + public static Filter oauth2(OAuth2AuthenticationProvider authProvider, + OAuth2Credentials credentials, + Function> userFunction, + Function> errorFunction) { + Objects.requireNonNull(authProvider, "auth provider can not be null"); + Objects.requireNonNull(credentials, "credentials can not be null"); + Objects.requireNonNull(userFunction, "user function can not be null"); + Objects.requireNonNull(errorFunction, "error function cannot be null"); + + return (route) -> (request) -> { + if (request.path().equals(credentials.getRedirectUri())) { + return FXFuture.fromJava(authProvider.authenticate(credentials)) + .flatMap(user -> { + userFunction.apply(user); + return route.apply(request); + }) + .flatExceptionally(ex -> { + errorFunction.apply(ex); + return route.apply(request); + }); + } else { + return route.apply(request); + } + }; + } + + private AuthFilters() { + // Hide the default constructor. + } +} diff --git a/settings.gradle b/settings.gradle index 027e2d3f..f6b9b95e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ include "jpro-auth:core" +include "jpro-auth:routing" include "jpro-auth:example" include "jpro-file" include "jpro-file:example"