diff --git a/legend-sdlc-server/pom.xml b/legend-sdlc-server/pom.xml index b9dbe804e7..bca561e4b1 100644 --- a/legend-sdlc-server/pom.xml +++ b/legend-sdlc-server/pom.xml @@ -129,6 +129,16 @@ + + org.finos.legend.shared + legend-shared-pac4j + + + com.zaxxer + HikariCP + + + org.finos.legend.shared legend-shared-pac4j-kerberos @@ -177,6 +187,11 @@ + + org.pac4j.jax-rs + core + + com.google.inject guice diff --git a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/auth/GitLabSessionBuilder.java b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/auth/GitLabSessionBuilder.java index e09d331677..556f11b5e1 100644 --- a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/auth/GitLabSessionBuilder.java +++ b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/auth/GitLabSessionBuilder.java @@ -25,7 +25,7 @@ import java.time.Instant; import java.util.Objects; -class GitLabSessionBuilder extends SessionBuilder +public class GitLabSessionBuilder extends SessionBuilder { private final GitLabTokenManager tokenManager; @@ -130,7 +130,7 @@ static boolean isSupportedProfile(CommonProfile profile) return (profile instanceof KerberosProfile) || (profile instanceof OidcProfile) || (profile instanceof GitlabPersonalAccessTokenProfile); } - static GitLabSessionBuilder newBuilder(GitLabAppInfo appInfo) + public static GitLabSessionBuilder newBuilder(GitLabAppInfo appInfo) { return new GitLabSessionBuilder(GitLabTokenManager.newTokenManager(appInfo)); } diff --git a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/resources/GitLabAuthCheckResource.java b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/resources/GitLabAuthCheckResource.java index 7d769fdc09..6cb68ce8b7 100644 --- a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/resources/GitLabAuthCheckResource.java +++ b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/gitlab/resources/GitLabAuthCheckResource.java @@ -21,7 +21,7 @@ import org.finos.legend.sdlc.server.gitlab.auth.GitLabSession; import org.finos.legend.sdlc.server.gitlab.auth.GitLabUserContext; import org.finos.legend.sdlc.server.resources.BaseResource; -import org.finos.legend.sdlc.server.tools.SessionUtil; +import org.finos.legend.sdlc.server.tools.SessionProvider; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; @@ -31,23 +31,30 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import static org.finos.legend.sdlc.server.auth.LegendSDLCWebFilter.SESSION_ATTRIBUTE; + @Path("/auth") public class GitLabAuthCheckResource extends BaseResource { - private final HttpServletRequest httpRequest; private final HttpServletResponse httpResponse; private final GitLabAuthorizerManager authorizerManager; private final GitLabAppInfo appInfo; + private final SessionProvider sessionProvider; @Inject - public GitLabAuthCheckResource(HttpServletRequest httpRequest, HttpServletResponse httpResponse, GitLabAuthorizerManager authorizerManager, GitLabAppInfo appInfo) + public GitLabAuthCheckResource(HttpServletRequest httpRequest, + HttpServletResponse httpResponse, + GitLabAuthorizerManager authorizerManager, + GitLabAppInfo appInfo, + SessionProvider sessionProvider) { super(); this.httpRequest = httpRequest; this.httpResponse = httpResponse; this.authorizerManager = authorizerManager; this.appInfo = appInfo; + this.sessionProvider = sessionProvider; } @GET @@ -57,7 +64,13 @@ public boolean isAuthorized() { return executeWithLogging("checking authorization", () -> { - Session session = SessionUtil.findSession(httpRequest); + Session session = SessionProvider.findSession(httpRequest); + + if (session == null) + { + session = sessionProvider.getSessionFromSessionStore(httpRequest, httpResponse, appInfo); + httpRequest.setAttribute(SESSION_ATTRIBUTE, session); + } if (session instanceof GitLabSession) { diff --git a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/AbstractBaseModule.java b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/AbstractBaseModule.java index 569fafd1cc..bf5c338a56 100644 --- a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/AbstractBaseModule.java +++ b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/AbstractBaseModule.java @@ -17,6 +17,7 @@ import com.google.inject.Binder; import com.google.inject.Provides; import com.hubspot.dropwizard.guicier.DropwizardAwareModule; +import org.eclipse.collections.api.factory.Maps; import org.finos.legend.sdlc.server.BaseLegendSDLCServer; import org.finos.legend.sdlc.server.BaseServer.ServerInfo; import org.finos.legend.sdlc.server.config.LegendSDLCServerConfiguration; @@ -224,6 +225,14 @@ import org.finos.legend.sdlc.server.resources.workflow.project.user.WorkspaceWorkflowsResource; import org.finos.legend.sdlc.server.resources.workspace.project.user.WorkspacesResource; import org.finos.legend.sdlc.server.tools.BackgroundTaskProcessor; +import org.finos.legend.sdlc.server.tools.SessionProvider; +import org.finos.legend.server.pac4j.LegendPac4jConfiguration; +import org.finos.legend.server.pac4j.hazelcaststore.HazelcastSessionStore; +import org.pac4j.core.context.J2EContext; +import org.pac4j.core.context.session.J2ESessionStore; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.jax.rs.pac4j.JaxRsContext; +import org.pac4j.jax.rs.servlet.pac4j.ServletSessionStore; import java.util.Collections; import java.util.List; @@ -257,6 +266,7 @@ public void configure(Binder binder) binder.bind(LegendSDLCServerFeaturesConfiguration.class).toProvider(this::getFeaturesConfiguration); binder.bind(BackgroundTaskProcessor.class).toProvider(this.server::getBackgroundTaskProcessor); binder.bind(ProjectStructurePlatformExtensions.class).toInstance(buildProjectStructurePlatformExtensions()); + binder.bind(SessionProvider.class).toProvider(this::getSessionProvider); bindResources(binder); } @@ -539,6 +549,25 @@ private AuthClientInjector resolveAuthClientInjector() return builder -> builder; } + private SessionProvider getSessionProvider() + { + LegendPac4jConfiguration config = getConfiguration().getPac4jConfiguration(); + return new SessionProvider(getSessionStoreFromConfig(config)); + } + + private SessionStore getSessionStoreFromConfig(LegendPac4jConfiguration config) + { + if (config.getHazelcastSession() != null && config.getHazelcastSession().isEnabled()) + { + return new HazelcastSessionStore( + config.getHazelcastSession().getConfigFilePath(), + Maps.immutable.with( + J2EContext.class, new J2ESessionStore(), + JaxRsContext.class, new ServletSessionStore()).castToMap()); + } + return null; + } + @Provides @Named("applicationName") public String provideApplicationName(LegendSDLCServerConfiguration configuration) diff --git a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/UserContext.java b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/UserContext.java index c84d3b1fd2..2159c428f2 100644 --- a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/UserContext.java +++ b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/guice/UserContext.java @@ -17,13 +17,12 @@ import com.google.inject.servlet.RequestScoped; import org.finos.legend.sdlc.server.auth.Session; import org.finos.legend.sdlc.server.error.LegendSDLCServerException; -import org.finos.legend.sdlc.server.tools.SessionUtil; +import org.finos.legend.sdlc.server.tools.SessionProvider; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - @RequestScoped public class UserContext { @@ -37,7 +36,7 @@ public UserContext(HttpServletRequest httpRequest, HttpServletResponse httpRespo { this.httpRequest = httpRequest; this.httpResponse = httpResponse; - this.session = LegendSDLCServerException.validateNonNull(SessionUtil.findSession(httpRequest), "Invalid request"); + this.session = LegendSDLCServerException.validateNonNull(SessionProvider.findSession(httpRequest), "Invalid request"); } public String getCurrentUser() diff --git a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionUtil.java b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionProvider.java similarity index 65% rename from legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionUtil.java rename to legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionProvider.java index a2c35c5622..08c64c1f3c 100644 --- a/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionUtil.java +++ b/legend-sdlc-server/src/main/java/org/finos/legend/sdlc/server/tools/SessionProvider.java @@ -16,18 +16,56 @@ import org.finos.legend.sdlc.server.auth.LegendSDLCWebFilter; import org.finos.legend.sdlc.server.auth.Session; +import org.finos.legend.sdlc.server.gitlab.GitLabAppInfo; +import org.finos.legend.sdlc.server.gitlab.auth.GitLabSessionBuilder; +import org.finos.legend.server.pac4j.gitlab.GitlabClient; +import org.pac4j.core.context.J2EContext; +import org.pac4j.core.context.Pac4jConstants; +import org.pac4j.core.context.WebContext; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.core.profile.CommonProfile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.util.Map; -public class SessionUtil +public class SessionProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(SessionUtil.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SessionProvider.class); + + private final SessionStore sessionStore; + + public SessionProvider(SessionStore sessionStore) + { + this.sessionStore = sessionStore; + } + + public Session getSessionFromSessionStore(HttpServletRequest httpRequest, HttpServletResponse httpResponse, GitLabAppInfo appInfo) + { + if (sessionStore != null) + { + WebContext context = new J2EContext(httpRequest, httpResponse); + Map profileMap = + (Map) sessionStore.get(context, Pac4jConstants.USER_PROFILES); + + if (profileMap != null) + { + CommonProfile profile = profileMap.get(GitlabClient.GITLAB_CLIENT_NAME); + if (profile != null) + { + return GitLabSessionBuilder.newBuilder(appInfo).withProfile(profile).build(); + } + } + } + + return null; + } public static Session findSession(ServletRequest request) { diff --git a/legend-sdlc-server/src/test/resources/config-sample.yaml b/legend-sdlc-server/src/test/resources/config-sample.yaml index efd3b427b9..5f4dba93fc 100644 --- a/legend-sdlc-server/src/test/resources/config-sample.yaml +++ b/legend-sdlc-server/src/test/resources/config-sample.yaml @@ -59,6 +59,9 @@ pac4j: secret: $APP_SECRET discoveryUri: https://$GITLAB_HOST/.well-known/openid-configuration scope: openid profile api +# hazelcastSession: +# enabled: true +# configFilePath: legend-sdlc-server/src/test/resources/hazelcast.yaml bypassPaths: - /api/info - /api/server/info diff --git a/legend-sdlc-server/src/test/resources/hazelcast.yaml b/legend-sdlc-server/src/test/resources/hazelcast.yaml new file mode 100644 index 0000000000..30a212dae1 --- /dev/null +++ b/legend-sdlc-server/src/test/resources/hazelcast.yaml @@ -0,0 +1,31 @@ +# Copyright 2023 Goldman Sachs +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +hazelcast: + cluster-name: legend-hazelcast-session-store-cluster + instance-name: legend-hazelcast-session-store + network: + port: + port: 5701 + auto-increment: false + join: + multicast: + enabled: false + tcp-ip: + enabled: true + member-list: + - localhost + map: + session-store: + time-to-live-seconds: 7200 diff --git a/pom.xml b/pom.xml index f2136170d7..3fd310be90 100644 --- a/pom.xml +++ b/pom.xml @@ -70,9 +70,9 @@ [11,12),[17,18) - 4.24.0 + 4.25.0 4.5.11 - 0.23.8 + 0.24.0 1.15 @@ -1379,6 +1379,11 @@ pac4j-oidc ${pac4j.version} + + org.pac4j.jax-rs + core + 3.0.0 + org.slf4j