diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/CallbackFilterImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/CallbackFilterImpl.java index d382c79f69..86d615b0f0 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/CallbackFilterImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/CallbackFilterImpl.java @@ -5,12 +5,13 @@ */ package io.jooby.internal.pac4j; +import org.pac4j.core.adapter.FrameworkAdapter; import org.pac4j.core.config.Config; import edu.umd.cs.findbugs.annotations.NonNull; import io.jooby.Context; import io.jooby.Route; -import io.jooby.pac4j.Pac4jContext; +import io.jooby.pac4j.Pac4jFrameworkParameters; import io.jooby.pac4j.Pac4jOptions; public class CallbackFilterImpl implements Route.Handler { @@ -26,19 +27,16 @@ public CallbackFilterImpl(Config config, Pac4jOptions options) { @NonNull @Override public Object apply(@NonNull Context ctx) throws Exception { - Pac4jContext pac4j = Pac4jContext.create(ctx); - - Object result = + FrameworkAdapter.INSTANCE.applyDefaultSettingsIfUndefined(config); + var result = config .getCallbackLogic() .perform( - pac4j, - pac4j.getSessionStore(), config, - config.getHttpActionAdapter(), options.getDefaultUrl(), - options.getSaveInSession(), - options.getDefaultClient()); + options.getRenewSession(), + options.getDefaultClient(), + Pac4jFrameworkParameters.create(ctx)); return result == null ? ctx : result; } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/ContextFactoryImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/ContextFactoryImpl.java new file mode 100644 index 0000000000..c28b4466c9 --- /dev/null +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/ContextFactoryImpl.java @@ -0,0 +1,23 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby.internal.pac4j; + +import org.pac4j.core.context.FrameworkParameters; +import org.pac4j.core.context.WebContext; +import org.pac4j.core.context.WebContextFactory; + +import io.jooby.pac4j.Pac4jContext; +import io.jooby.pac4j.Pac4jFrameworkParameters; + +public class ContextFactoryImpl implements WebContextFactory { + @Override + public WebContext newContext(FrameworkParameters parameters) { + if (parameters instanceof Pac4jFrameworkParameters params) { + return Pac4jContext.create(params.getContext()); + } + throw new IllegalArgumentException("Can't create context from: " + parameters); + } +} diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/GrantAccessAdapterImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/GrantAccessAdapterImpl.java index 2dce86674b..c64557956e 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/GrantAccessAdapterImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/GrantAccessAdapterImpl.java @@ -6,41 +6,60 @@ package io.jooby.internal.pac4j; import java.util.Collection; -import java.util.Iterator; import org.pac4j.core.context.WebContext; import org.pac4j.core.context.session.SessionStore; import org.pac4j.core.engine.SecurityGrantedAccessAdapter; +import org.pac4j.core.exception.http.WithLocationAction; import org.pac4j.core.profile.UserProfile; +import org.pac4j.core.util.Pac4jConstants; import io.jooby.Context; import io.jooby.Route; -import io.jooby.pac4j.Pac4jContext; +import io.jooby.SneakyThrows; +import io.jooby.pac4j.Pac4jOptions; public class GrantAccessAdapterImpl implements SecurityGrantedAccessAdapter { private final Context ctx; - private final Route.Handler next; + private final Pac4jOptions config; + private final SneakyThrows.Function2 next; - public GrantAccessAdapterImpl(Context ctx, Route.Handler next) { + public GrantAccessAdapterImpl(Context ctx, Pac4jOptions config) { + this( + ctx, + config, + (context, sessionStore) -> { + var requestedUrl = + sessionStore + .get(context, Pac4jConstants.REQUESTED_URL) + .filter(WithLocationAction.class::isInstance) + .map(it -> ((WithLocationAction) it).getLocation()) + .orElse(config.getDefaultUrl()); + return ctx.sendRedirect(requestedUrl); + }); + } + + public GrantAccessAdapterImpl(Context ctx, Pac4jOptions config, Route.Handler next) { + this(ctx, config, (context, sessionStore) -> next.apply(ctx)); + } + + private GrantAccessAdapterImpl( + Context ctx, + Pac4jOptions config, + SneakyThrows.Function2 next) { this.ctx = ctx; + this.config = config; this.next = next; } @Override public Object adapt( - WebContext webContext, - SessionStore sessionStore, - Collection profiles, - Object... objects) + WebContext context, SessionStore sessionStore, Collection profiles) throws Exception { - Iterator iterator = profiles.iterator(); + var iterator = profiles.iterator(); if (iterator.hasNext()) { - ((Pac4jContext) webContext).getContext().setUser(iterator.next()); + ctx.setUser(iterator.next()); } - return next.apply(ctx); - } - - public static GrantAccessAdapterImpl redirect(Context context, String redirectTo) { - return new GrantAccessAdapterImpl(context, ctx -> ctx.sendRedirect(redirectTo)); + return next.apply(context, sessionStore); } } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/LogoutImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/LogoutImpl.java index 5879584443..5f4278c8d1 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/LogoutImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/LogoutImpl.java @@ -5,12 +5,13 @@ */ package io.jooby.internal.pac4j; +import org.pac4j.core.adapter.FrameworkAdapter; import org.pac4j.core.config.Config; import edu.umd.cs.findbugs.annotations.NonNull; import io.jooby.Context; import io.jooby.Route; -import io.jooby.pac4j.Pac4jContext; +import io.jooby.pac4j.Pac4jFrameworkParameters; import io.jooby.pac4j.Pac4jOptions; public class LogoutImpl implements Route.Handler { @@ -26,23 +27,21 @@ public LogoutImpl(Config config, Pac4jOptions options) { @NonNull @Override public Object apply(@NonNull Context ctx) throws Exception { - String redirectTo = (String) ctx.getAttributes().get("pac4j.logout.redirectTo"); - if (redirectTo == null || redirectTo.length() == 0) { + FrameworkAdapter.INSTANCE.applyDefaultSettingsIfUndefined(config); + var redirectTo = (String) ctx.getAttributes().get("pac4j.logout.redirectTo"); + if (redirectTo == null || redirectTo.isEmpty()) { redirectTo = options.getDefaultUrl(); } redirectTo = ctx.getRequestURL(redirectTo); - Pac4jContext pac4jContext = Pac4jContext.create(ctx); return config .getLogoutLogic() .perform( - pac4jContext, - pac4jContext.getSessionStore(), config, - config.getHttpActionAdapter(), redirectTo, - null, + options.getLogoutUrlPattern(), options.isLocalLogout(), options.isDestroySession(), - options.isCentralLogout()); + options.isCentralLogout(), + Pac4jFrameworkParameters.create(ctx)); } } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/Pac4jCurrentUser.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/Pac4jCurrentUser.java index 8eb2c40ab5..017f2338d0 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/Pac4jCurrentUser.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/Pac4jCurrentUser.java @@ -8,18 +8,24 @@ import java.util.function.Function; import org.pac4j.core.config.Config; -import org.pac4j.core.profile.ProfileManager; import io.jooby.Context; import io.jooby.pac4j.Pac4jContext; public class Pac4jCurrentUser implements Function { + private final Config config; + + public Pac4jCurrentUser(Config config) { + this.config = config; + } + @Override public Object apply(Context ctx) { - Pac4jContext pac4jContext = Pac4jContext.create(ctx); - ProfileManager pm = new ProfileManager(pac4jContext, pac4jContext.getSessionStore()); - pm.setConfig(ctx.require(Config.class)); - return pm.getProfile().orElse(null); + var pmf = config.getProfileManagerFactory(); + var pac4jContext = Pac4jContext.create(ctx); + var pm = pmf.apply(pac4jContext, pac4jContext.getSessionStore()); + var profile = pm.getProfile().orElse(null); + return profile; } } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SavedRequestHandlerImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SavedRequestHandlerImpl.java index 019eb3401e..528debedc1 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SavedRequestHandlerImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SavedRequestHandlerImpl.java @@ -7,26 +7,24 @@ import java.util.Set; -import org.pac4j.core.context.WebContext; -import org.pac4j.core.context.session.SessionStore; +import org.pac4j.core.context.CallContext; import org.pac4j.core.engine.savedrequest.DefaultSavedRequestHandler; -import io.jooby.Context; import io.jooby.pac4j.Pac4jContext; public class SavedRequestHandlerImpl extends DefaultSavedRequestHandler { - private Set excludes; + private final Set excludes; public SavedRequestHandlerImpl(Set excludes) { this.excludes = excludes; } @Override - public void save(WebContext webContext, SessionStore sessionStore) { - Pac4jContext pac4j = (Pac4jContext) webContext; - Context context = pac4j.getContext(); + public void save(CallContext ctx) { + var pac4j = (Pac4jContext) ctx.webContext(); + var context = pac4j.getContext(); if (!excludes.contains(context.getRequestPath())) { - super.save(webContext, sessionStore); + super.save(ctx); } } } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SecurityFilterImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SecurityFilterImpl.java index c7a5867fe4..9c93e6d3d7 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SecurityFilterImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SecurityFilterImpl.java @@ -10,29 +10,29 @@ import java.util.List; import java.util.function.Supplier; +import org.pac4j.core.adapter.FrameworkAdapter; import org.pac4j.core.client.finder.ClientFinder; import org.pac4j.core.client.finder.DefaultSecurityClientFinder; import org.pac4j.core.config.Config; import org.pac4j.core.engine.DefaultSecurityLogic; import org.pac4j.core.engine.SecurityLogic; -import org.pac4j.core.exception.http.WithLocationAction; import org.pac4j.core.util.Pac4jConstants; import edu.umd.cs.findbugs.annotations.NonNull; import io.jooby.Context; import io.jooby.Route; -import io.jooby.pac4j.Pac4jContext; +import io.jooby.pac4j.Pac4jFrameworkParameters; import io.jooby.pac4j.Pac4jOptions; public class SecurityFilterImpl implements Route.Filter, Route.Handler { - private String pattern; + private final String pattern; - private Config config; + private final Config config; - private Pac4jOptions options; + private final Pac4jOptions options; - private Supplier clients; + private final Supplier clients; private String authorizers; @@ -61,10 +61,10 @@ public void addAuthorizer(String authorizer) { public Route.Handler apply(@NonNull Route.Handler next) { return ctx -> { if (pattern == null) { - return perform(Pac4jContext.create(ctx), new GrantAccessAdapterImpl(ctx, next)); + return perform(ctx, new GrantAccessAdapterImpl(ctx, options, next)); } else { if (ctx.matches(pattern)) { - return perform(Pac4jContext.create(ctx), new GrantAccessAdapterImpl(ctx, next)); + return perform(ctx, new GrantAccessAdapterImpl(ctx, options, next)); } else { return next.apply(ctx); } @@ -74,32 +74,16 @@ public Route.Handler apply(@NonNull Route.Handler next) { @NonNull @Override public Object apply(@NonNull Context ctx) throws Exception { - Pac4jContext pac4j = Pac4jContext.create(ctx); - String requestedUrl = - (String) - pac4j - .getSessionStore() - .get(pac4j, Pac4jConstants.REQUESTED_URL) - .filter(WithLocationAction.class::isInstance) - .map(it -> ((WithLocationAction) it).getLocation()) - .orElse(options.getDefaultUrl()); - return perform(pac4j, GrantAccessAdapterImpl.redirect(ctx, requestedUrl)); + return perform(ctx, new GrantAccessAdapterImpl(ctx, options)); } - private Object perform(Pac4jContext ctx, GrantAccessAdapterImpl grantAccessAdapter) { - SecurityLogic securityLogic = config.getSecurityLogic(); - String clients = ctx.getContext().query(clientName(securityLogic)).value(this.clients.get()); - String authorizers = ofNullable(this.authorizers).orElse(NoopAuthorizer.NAME); + private Object perform(Context ctx, GrantAccessAdapterImpl accessAdapter) throws Exception { + FrameworkAdapter.INSTANCE.applyDefaultSettingsIfUndefined(config); + var securityLogic = config.getSecurityLogic(); + var clients = ctx.lookup(clientName(securityLogic)).value(this.clients.get()); + var authorizers = ofNullable(this.authorizers).orElse(NoopAuthorizer.NAME); return securityLogic.perform( - ctx, - ctx.getSessionStore(), - config, - grantAccessAdapter, - config.getHttpActionAdapter(), - clients, - authorizers, - null, - options.getMultiProfile()); + config, accessAdapter, clients, authorizers, null, Pac4jFrameworkParameters.create(ctx)); } private String clientName(SecurityLogic securityLogic) { diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SessionStoreFactoryImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SessionStoreFactoryImpl.java new file mode 100644 index 0000000000..6f19438fc2 --- /dev/null +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/SessionStoreFactoryImpl.java @@ -0,0 +1,22 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby.internal.pac4j; + +import org.pac4j.core.context.FrameworkParameters; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.core.context.session.SessionStoreFactory; + +import io.jooby.pac4j.Pac4jContext; + +public class SessionStoreFactoryImpl implements SessionStoreFactory { + @Override + public SessionStore newSessionStore(FrameworkParameters parameters) { + if (parameters instanceof Pac4jContext ctx) { + return ctx.getSessionStore(); + } + return new SessionStoreImpl(); + } +} diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/WebContextImpl.java b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/WebContextImpl.java index e0c8d450db..e231668092 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/WebContextImpl.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/internal/pac4j/WebContextImpl.java @@ -35,7 +35,7 @@ public WebContextImpl(Context context) { } @Override - public Context getContext() { + public @NonNull Context getContext() { return context; } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jFrameworkParameters.java b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jFrameworkParameters.java new file mode 100644 index 0000000000..d217e4042c --- /dev/null +++ b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jFrameworkParameters.java @@ -0,0 +1,36 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby.pac4j; + +import org.pac4j.core.context.FrameworkParameters; + +import io.jooby.Context; + +/** + * Provide access to HTTP context. + * + * @author edgar + * @since 3.5.0 + */ +public interface Pac4jFrameworkParameters extends FrameworkParameters { + /** + * HTTP context. + * + * @return HTTP context. + */ + Context getContext(); + + /** + * Creates a pac4j framework parameters. + * + * @param ctx HTTP Context. + * @return Framework parameters + */ + static Pac4jFrameworkParameters create(Context ctx) { + var custom = ctx.getRouter().getServices().getOrNull(Pac4jFrameworkParameters.class); + return custom != null ? custom : () -> ctx; + } +} diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jModule.java b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jModule.java index e1496f8495..e9125c3d3e 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jModule.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jModule.java @@ -22,16 +22,15 @@ import org.pac4j.core.client.Clients; import org.pac4j.core.client.DirectClient; import org.pac4j.core.config.Config; -import org.pac4j.core.engine.CallbackLogic; import org.pac4j.core.engine.DefaultCallbackLogic; import org.pac4j.core.engine.DefaultLogoutLogic; import org.pac4j.core.engine.DefaultSecurityLogic; import org.pac4j.core.engine.LogoutLogic; -import org.pac4j.core.engine.SecurityLogic; import org.pac4j.core.exception.http.ForbiddenAction; import org.pac4j.core.exception.http.UnauthorizedAction; import org.pac4j.core.http.adapter.HttpActionAdapter; import org.pac4j.core.http.url.UrlResolver; +import org.pac4j.core.profile.factory.ProfileManagerFactory; import org.pac4j.http.client.indirect.FormClient; import org.pac4j.http.credentials.authenticator.test.SimpleTestUsernamePasswordAuthenticator; @@ -43,17 +42,7 @@ import io.jooby.Route; import io.jooby.Router; import io.jooby.StatusCode; -import io.jooby.internal.pac4j.ActionAdapterImpl; -import io.jooby.internal.pac4j.CallbackFilterImpl; -import io.jooby.internal.pac4j.ClientReference; -import io.jooby.internal.pac4j.DevLoginForm; -import io.jooby.internal.pac4j.ForwardingAuthorizer; -import io.jooby.internal.pac4j.LogoutImpl; -import io.jooby.internal.pac4j.NoopAuthorizer; -import io.jooby.internal.pac4j.Pac4jCurrentUser; -import io.jooby.internal.pac4j.SavedRequestHandlerImpl; -import io.jooby.internal.pac4j.SecurityFilterImpl; -import io.jooby.internal.pac4j.UrlResolverImpl; +import io.jooby.internal.pac4j.*; /** * Pac4j module: https://jooby.io/modules/pac4j. @@ -369,26 +358,25 @@ public Pac4jModule client( } @Override - public void install(@NonNull Jooby application) throws Exception { - application.getServices().putIfAbsent(Pac4jOptions.class, options); + public void install(@NonNull Jooby app) throws Exception { + app.getServices().putIfAbsent(Pac4jOptions.class, options); - Clients clients = + var clients = ofNullable(pac4j.getClients()) - /** No client? set a default one: */ + /* No client? set a default one: */ .orElseGet(Clients::new); - /** No client instance added from DSL, init them from pac4j config. */ + /* No client instance added from DSL, init them from pac4j config. */ if (clientMap == null) { clientMap = initializeClients(pac4j); } - String contextPath = - application.getContextPath().equals("/") ? "" : application.getContextPath(); + var contextPath = app.getContextPath().equals("/") ? "" : app.getContextPath(); Map> allClients = new LinkedHashMap<>(); - /** Should add simple login form? */ - boolean devLogin = false; + /* Should add simple login form? */ + var devLogin = false; if (clientMap.isEmpty()) { devLogin = true; client( @@ -396,13 +384,12 @@ public void install(@NonNull Jooby application) throws Exception { new FormClient( contextPath + "/login", new SimpleTestUsernamePasswordAuthenticator())); } - com.typesafe.config.Config conf = application.getConfig(); - /** Initialize clients from DSL: */ - for (Map.Entry routing : clientMap.entrySet()) { - List localClients = - allClients.computeIfAbsent(routing.getKey(), k -> new ArrayList<>()); - ProtectedPath path = routing.getValue(); - for (Object candidate : path.clients) { + var conf = app.getConfig(); + /* Initialize clients from DSL: */ + for (var routing : clientMap.entrySet()) { + var localClients = allClients.computeIfAbsent(routing.getKey(), k -> new ArrayList<>()); + var path = routing.getValue(); + for (var candidate : path.clients) { if (candidate instanceof Class) { localClients.add(new ClientReference((Class) candidate)); } else if (candidate instanceof Client) { @@ -416,24 +403,24 @@ public void install(@NonNull Jooby application) throws Exception { allClients.put(routing.getKey(), localClients); // check for forwarding authorizers - for (String authorizerName : path.authorizers) { + for (var authorizerName : path.authorizers) { Authorizer authorizer = pac4j.getAuthorizers().get(authorizerName); if (authorizer instanceof ForwardingAuthorizer) { - ((ForwardingAuthorizer) authorizer).setRegistry(application); + ((ForwardingAuthorizer) authorizer).setRegistry(app); } } } pac4j.getAuthorizers().put(NoopAuthorizer.NAME, new NoopAuthorizer()); - /** Default callback URL if none was set at client level: */ + /* Default callback URL if none was set at client level: */ clients.setCallbackUrl( ofNullable(clients.getCallbackUrl()).orElse(contextPath + options.getCallbackPath())); - /** Default URL resolver if none was set at client level: */ + /* Default URL resolver if none was set at client level: */ clients.setUrlResolver( ofNullable(clients.getUrlResolver()).orElseGet(Pac4jModule::newUrlResolver)); - /** Set resolved clients: */ + /* Set resolved clients: */ clients.setClients( allClients.values().stream() .flatMap(List::stream) @@ -442,16 +429,16 @@ public void install(@NonNull Jooby application) throws Exception { .collect(Collectors.toList())); pac4j.setClients(clients); - /** Delay setting unresolved clients: */ - List unresolved = + /* Delay setting unresolved clients: */ + var unresolved = allClients.values().stream().flatMap(List::stream).filter(r -> !r.isResolved()).toList(); if (!unresolved.isEmpty()) { - application.onStarting( + app.onStarting( () -> { List clientList = new ArrayList<>(clients.getClients()); unresolved.stream() - .peek(r -> r.resolve(application::require)) + .peek(r -> r.resolve(app::require)) .map(ClientReference::getClient) .forEachOrdered(clientList::add); clients.setClients(clientList); @@ -464,15 +451,23 @@ public void install(@NonNull Jooby application) throws Exception { }); } - /** Set default http action adapter: */ + /* Set default http action adapter: */ pac4j.setHttpActionAdapter( ofNullable(pac4j.getHttpActionAdapter()).orElseGet(Pac4jModule::newActionAdapter)); + /* WebContextFactory: */ + pac4j.setWebContextFactory( + ofNullable(pac4j.getWebContextFactory()).orElseGet(ContextFactoryImpl::new)); + pac4j.setSessionStoreFactory( + ofNullable(pac4j.getSessionStoreFactory()).orElseGet(SessionStoreFactoryImpl::new)); + pac4j.setProfileManagerFactory( + ofNullable(pac4j.getProfileManagerFactory()).orElse(ProfileManagerFactory.DEFAULT)); + if (devLogin) { - application.get("/login", new DevLoginForm(pac4j, contextPath + options.getCallbackPath())); + app.get("/login", new DevLoginForm(pac4j, contextPath + options.getCallbackPath())); } - /** + /* * If we have multiple clients on specific paths, we collect those path and configure pac4j to * ignore them. So after login they are redirected to the requested url and not to one of this * sign-in endpoints. @@ -486,30 +481,30 @@ public void install(@NonNull Jooby application) throws Exception { * * So google and twitter paths are never saved as requested urls. */ - Set excludes = + var excludes = allClients.keySet().stream() .filter(it -> !it.equals("*")) .map(it -> contextPath + it) .collect(Collectors.toSet()); - CallbackLogic callbackLogic = pac4j.getCallbackLogic(); + var callbackLogic = pac4j.getCallbackLogic(); if (callbackLogic == null) { pac4j.setCallbackLogic(newCallbackLogic(excludes)); } var direct = clients.getClients().stream().allMatch(it -> it instanceof DirectClient); if (!direct || options.isForceCallbackRoutes()) { CallbackFilterImpl callbackFilter = new CallbackFilterImpl(pac4j, options); - application.get(options.getCallbackPath(), callbackFilter); - application.post(options.getCallbackPath(), callbackFilter); + app.get(options.getCallbackPath(), callbackFilter); + app.post(options.getCallbackPath(), callbackFilter); } - SecurityLogic securityLogic = pac4j.getSecurityLogic(); + var securityLogic = pac4j.getSecurityLogic(); if (securityLogic == null) { pac4j.setSecurityLogic(newSecurityLogic(excludes)); } /** For each client to a specific path, add a security handler. */ - for (Map.Entry> entry : allClients.entrySet()) { + for (var entry : allClients.entrySet()) { String pattern = entry.getKey(); if (!pattern.equals("*")) { List keys = Router.pathKeys(pattern); @@ -521,11 +516,11 @@ public void install(@NonNull Jooby application) throws Exception { options, lazyClientNameList(entry.getValue()), clientMap.get(pattern).authorizers); - application.get(pattern, securityFilter); + app.get(pattern, securityFilter); // POST for direct authentication - application.post(pattern, securityFilter); + app.post(pattern, securityFilter); } else { - application.use( + app.use( new SecurityFilterImpl( pattern, pac4j, @@ -537,12 +532,12 @@ public void install(@NonNull Jooby application) throws Exception { } /* Is there is a global client, use it as decorator/filter (default client): */ - List defaultSecurityFilter = allClients.get("*"); + var defaultSecurityFilter = allClients.get("*"); if (defaultSecurityFilter != null) { if (options.getDefaultClient() == null && defaultSecurityFilter.get(0).isResolved()) { options.setDefaultClient(defaultSecurityFilter.get(0).getClient().getName()); } - application.use( + app.use( new SecurityFilterImpl( null, pac4j, @@ -551,26 +546,26 @@ public void install(@NonNull Jooby application) throws Exception { clientMap.get("*").authorizers)); } - /** Logout configuration: */ - LogoutLogic logoutLogic = pac4j.getLogoutLogic(); + /* Logout configuration: */ + var logoutLogic = pac4j.getLogoutLogic(); if (logoutLogic == null) { pac4j.setLogoutLogic(newLogoutLogic()); } if (!direct || options.isForceLogoutRoutes()) { - application.get(options.getLogoutPath(), new LogoutImpl(pac4j, options)); + app.get(options.getLogoutPath(), new LogoutImpl(pac4j, options)); } - /** Better response code for some errors. */ - application.errorCode(UnauthorizedAction.class, StatusCode.UNAUTHORIZED); - application.errorCode(ForbiddenAction.class, StatusCode.FORBIDDEN); + /* Better response code for some errors. */ + app.errorCode(UnauthorizedAction.class, StatusCode.UNAUTHORIZED); + app.errorCode(ForbiddenAction.class, StatusCode.FORBIDDEN); - /** Compute default url as next available route. We only select static path patterns. */ + /* Compute default url as next available route. We only select static path patterns. */ if (options.getDefaultUrl() == null) { - int index = application.getRoutes().size(); - application.onStarting( + int index = app.getRoutes().size(); + app.onStarting( () -> { - List routes = application.getRoutes(); - String defaultUrl = application.getContextPath(); + List routes = app.getRoutes(); + String defaultUrl = app.getContextPath(); if (index < routes.size()) { Route route = routes.get(index); if (route.getPathKeys().isEmpty()) { @@ -581,10 +576,10 @@ public void install(@NonNull Jooby application) throws Exception { }); } - application.getServices().put(Config.class, pac4j); + app.getServices().put(Config.class, pac4j); - /** Set current user provider */ - application.setCurrentUser(new Pac4jCurrentUser()); + /* Set current user provider */ + app.setCurrentUser(new Pac4jCurrentUser(pac4j)); // cleanup clientMap.clear(); } diff --git a/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jOptions.java b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jOptions.java index 08d7856c48..0ec9827f82 100644 --- a/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jOptions.java +++ b/modules/jooby-pac4j/src/main/java/io/jooby/pac4j/Pac4jOptions.java @@ -36,6 +36,8 @@ public class Pac4jOptions { private boolean localLogout = true; + private String logoutUrlPattern; + private boolean destroySession = true; private boolean centralLogout; @@ -304,4 +306,25 @@ public boolean isForceLogoutRoutes() { this.forceLogoutRoutes = forceLogoutRoutes; return this; } + + /** + * It’s the logout URL pattern that the url parameter must match. It is an optional parameter and + * only relative URLs are allowed by default. + * + * @return Logout URL Pattern. + */ + public @Nullable String getLogoutUrlPattern() { + return logoutUrlPattern; + } + + /** + * It’s the logout URL pattern that the url parameter must match. It is an optional parameter and + * only relative URLs are allowed by default. + * + * @param logoutUrlPattern It’s the logout URL pattern that the url parameter must match. It is an + * optional parameter and only relative URLs are allowed by default. + */ + public void setLogoutUrlPattern(String logoutUrlPattern) { + this.logoutUrlPattern = logoutUrlPattern; + } } diff --git a/pom.xml b/pom.xml index 7ce0f9301f..e4dfad93ce 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ 4.12.0 0.12.6 - 5.7.0 + 6.0.6 2.3.2 9.2.1 8.0.1 diff --git a/tests/src/test/java/io/jooby/test/Issue3328.java b/tests/src/test/java/io/jooby/test/Issue3328.java index d829a89e55..4d2d529faf 100644 --- a/tests/src/test/java/io/jooby/test/Issue3328.java +++ b/tests/src/test/java/io/jooby/test/Issue3328.java @@ -7,10 +7,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Optional; import java.util.UUID; import org.pac4j.core.credentials.TokenCredentials; import org.pac4j.core.profile.BasicUserProfile; +import org.pac4j.core.profile.creator.ProfileCreator; import org.pac4j.http.client.direct.HeaderClient; import io.jooby.junit.ServerTest; @@ -31,11 +33,13 @@ public void pac4jShouldWorkWithSignedSession(ServerTestRunner runner) { conf -> new HeaderClient( "user-id", - (credentials, context, sessionStore) -> { - var profile = new BasicUserProfile(); - profile.setId(((TokenCredentials) credentials).getToken()); - credentials.setUserProfile(profile); - }))); + (ProfileCreator) + (ctx, credentials) -> { + var profile = new BasicUserProfile(); + profile.setId(((TokenCredentials) credentials).getToken()); + credentials.setUserProfile(profile); + return Optional.of(profile); + }))); app.get("/i3328", ctx -> ((BasicUserProfile) ctx.getUser()).getId()); })