From 434af0fc07ca5d8c75991459a899ce957c041e47 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Thu, 11 Jan 2024 21:37:53 +0400 Subject: [PATCH 01/33] CB-4389. Converted reverse proxy to identity provider --- .../cloudbeaver/service/auth/RPConstants.java | 10 ++++++ .../service/auth/RPSessionHandler.java | 35 +++++++++++++++---- .../auth/ReverserProxyConfigurator.java | 26 ++++++++++++++ .../plugin.xml | 11 ++++++ 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java new file mode 100644 index 0000000000..ab638eb311 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -0,0 +1,10 @@ +package io.cloudbeaver.service.auth; + +interface RPConstants { + String PARAM_LOGOUT_URL = "logout-url"; + String PARAM_USER = "user-header"; + String PARAM_TEAM = "team-header"; + String PARAM_FIRST_NAME = "first-name-header"; + String PARAM_LAST_NAME = "last-name-header"; + String PARAM_ROLE_NAME = "role-header"; +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 9128e03eab..8959e255ab 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -31,6 +31,7 @@ import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.auth.SMAuthInfo; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import org.jkiss.dbeaver.model.security.SMConstants; import org.jkiss.dbeaver.model.security.SMController; import org.jkiss.dbeaver.model.security.SMStandardMeta; @@ -62,22 +63,37 @@ public boolean handleSessionOpen(WebSession webSession, HttpServletRequest reque return false; } - public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @NotNull WebSession webSession) throws DBWebException { + public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @NotNull WebSession webSession) throws DBException { SMController securityController = webSession.getSecurityController(); WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(RPAuthProvider.AUTH_PROVIDER); if (authProvider == null) { throw new DBWebException("Auth provider " + RPAuthProvider.AUTH_PROVIDER + " not found"); } SMAuthProviderExternal authProviderExternal = (SMAuthProviderExternal) authProvider.getInstance(); - String userName = request.getHeader(RPAuthProvider.X_USER); - String teams = request.getHeader(RPAuthProvider.X_TEAM); + SMAuthProviderCustomConfiguration configuration = WebAppUtils.getWebAuthApplication() + .getAuthConfiguration() + .getAuthCustomConfigurations() + .stream() + .filter(p -> p.getProvider().equals(authProvider.toString())) + .findFirst() + .orElse(null); + Map paramConfigMap = new HashMap<>(); + if (configuration != null) { + authProvider.getConfigurationParameters().forEach(p -> + paramConfigMap.put(p.getId(), configuration.getParameters().get(p.getId()) + )); + } + String userName = request.getHeader( + resolveParam(paramConfigMap.get(RPConstants.PARAM_USER), RPAuthProvider.X_USER) + ); + String teams = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_TEAM), RPAuthProvider.X_TEAM)); if (CommonUtils.isEmpty(teams)) { // backward compatibility teams = request.getHeader(RPAuthProvider.X_ROLE); } - String role = request.getHeader(RPAuthProvider.X_ROLE_TE); - String firstName = request.getHeader(RPAuthProvider.X_FIRST_NAME); - String lastName = request.getHeader(RPAuthProvider.X_LAST_NAME); + String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); + String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); + String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -120,4 +136,11 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not public boolean handleSessionClose(WebSession webSession) throws DBException, IOException { return false; } + + private String resolveParam(Object value, String defaultValue) { + if (value != null && !value.toString().isEmpty()) { + return value.toString(); + } + return defaultValue; + } } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java new file mode 100644 index 0000000000..567be05983 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java @@ -0,0 +1,26 @@ +package io.cloudbeaver.service.auth; + +import io.cloudbeaver.model.app.WebAppConfiguration; +import io.cloudbeaver.model.app.WebApplication; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.service.DBWServiceServerConfigurator; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; + +public class ReverserProxyConfigurator implements DBWServiceServerConfigurator { + @Override + public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { + + } + + @Override + public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { + DBWServiceServerConfigurator.super.migrateConfigurationIfNeeded(application); // + } + + @Override + public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { + + } +} diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index 88eff847ae..d992e5a5f4 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -18,6 +18,7 @@ @@ -25,6 +26,16 @@ + + + + + + + + + + From 1dc6a119c2518d6d8355b07caf886ad653c257bc Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 12 Jan 2024 15:51:23 +0400 Subject: [PATCH 02/33] CB-4039 build logout redirect link on backend side --- .../auth/SMAuthProviderFederated.java | 17 ++++++++---- .../model/session/WebAuthInfo.java | 2 -- .../cloudbeaver/model/session/WebSession.java | 12 ++++++--- .../WebAuthProviderConfiguration.java | 2 +- .../schema/service.auth.graphqls | 6 ++++- .../service/auth/DBWServiceAuth.java | 6 ++++- .../service/auth/WebLogoutInfo.java | 24 +++++++++++++++++ .../service/auth/impl/WebServiceAuthImpl.java | 26 +++++++++++++++++-- .../CBEmbeddedSecurityController.java | 12 ++++++--- 9 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebLogoutInfo.java diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java index 1747122729..1ec4d407ed 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java @@ -19,6 +19,7 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import java.util.Map; @@ -28,15 +29,21 @@ */ public interface SMAuthProviderFederated { - /** - * Returns new identifying credentials which can be used to find/create user in database - */ @NotNull String getSignInLink(String id, @NotNull Map providerConfig) throws DBException; - + /** + * @return a common link for logout, not related with the user context + */ @NotNull - String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + + default String getUserSignOutLink( + @NotNull SMAuthProviderCustomConfiguration providerConfig, + @NotNull Map userCredentials + ) throws DBException { + return getCommonSignOutLink(providerConfig.getId(), providerConfig.getParameters()); + } @Nullable String getMetadataLink(String id, @NotNull Map providerConfig) throws DBException; diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebAuthInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebAuthInfo.java index 0d2abe7549..5c178215d9 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebAuthInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebAuthInfo.java @@ -131,8 +131,6 @@ void closeAuth() { authProviderInstance.closeSession(session, authSession); } catch (Exception e) { log.error(e); - } finally { - authSession = null; } } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index fbff2dbbda..2145ccc568 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -614,7 +614,7 @@ public void close() { super.close(); } - private void clearAuthTokens() throws DBException { + private List clearAuthTokens() throws DBException { ArrayList tokensCopy; synchronized (authTokens) { tokensCopy = new ArrayList<>(this.authTokens); @@ -623,6 +623,7 @@ private void clearAuthTokens() throws DBException { removeAuthInfo(ai); } resetAuthToken(); + return tokensCopy; } public DBRProgressMonitor getProgressMonitor() { @@ -873,18 +874,23 @@ private void removeAuthInfo(WebAuthInfo oldAuthInfo) { } } - public void removeAuthInfo(String providerId) throws DBException { + public List removeAuthInfo(String providerId) throws DBException { + List oldInfo; if (providerId == null) { - clearAuthTokens(); + oldInfo = clearAuthTokens(); } else { WebAuthInfo authInfo = getAuthInfo(providerId); if (authInfo != null) { removeAuthInfo(authInfo); + oldInfo = List.of(authInfo); + } else { + oldInfo = List.of(); } } if (authTokens.isEmpty()) { resetUserState(); } + return oldInfo; } public List getContextCredentialsProviders() { diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java index 266382b8cc..46712a0a59 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java @@ -86,7 +86,7 @@ private String buildRedirectUrl(String baseUrl) { public String getSignOutLink() throws DBException { SMAuthProvider instance = providerDescriptor.getInstance(); return instance instanceof SMAuthProviderFederated - ? ((SMAuthProviderFederated) instance).getSignOutLink(getId(), config.getParameters()) + ? ((SMAuthProviderFederated) instance).getCommonSignOutLink(getId(), config.getParameters()) : null; } diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index 82530a3bc7..b5e8db47fc 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -86,6 +86,10 @@ type AuthInfo { userTokens: [UserAuthToken!] } +type LogoutInfo { + redirectLinks: [String!]! +} + type UserAuthToken { # Auth provider used for authorization authProvider: ID! @@ -139,7 +143,7 @@ extend type Query { authUpdateStatus(authId: ID!, linkUser: Boolean): AuthInfo! # Logouts user. If provider not specified then all authorizations are revoked from session. - authLogout(provider: ID, configuration: ID): Boolean + authLogout(provider: ID, configuration: ID): LogoutInfo # Active user information. null is no user was authorized within session activeUser: UserInfo diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java index 777b0dc7b4..c0c9c22c24 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java @@ -45,7 +45,11 @@ WebAuthStatus authLogin( WebAuthStatus authUpdateStatus(@NotNull WebSession webSession, @NotNull String authId, boolean linkWithActiveUser) throws DBWebException; @WebAction(authRequired = false) - void authLogout(@NotNull WebSession webSession, @Nullable String providerId, @Nullable String configurationId) throws DBWebException; + WebLogoutInfo authLogout( + @NotNull WebSession webSession, + @Nullable String providerId, + @Nullable String configurationId + ) throws DBWebException; @WebAction(authRequired = false) WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebLogoutInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebLogoutInfo.java new file mode 100644 index 0000000000..9e8aaefb3d --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebLogoutInfo.java @@ -0,0 +1,24 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ +package io.cloudbeaver.service.auth; + +import org.jkiss.code.NotNull; + +import java.util.List; + +public record WebLogoutInfo(@NotNull List redirectLinks) { +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index e16245f8dd..0d1f3db208 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -18,6 +18,7 @@ import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebServiceUtils; +import io.cloudbeaver.auth.SMAuthProviderFederated; import io.cloudbeaver.auth.provider.local.LocalAuthProvider; import io.cloudbeaver.model.WebPropertyInfo; import io.cloudbeaver.model.session.WebAuthInfo; @@ -31,6 +32,7 @@ import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.service.auth.DBWServiceAuth; import io.cloudbeaver.service.auth.WebAuthStatus; +import io.cloudbeaver.service.auth.WebLogoutInfo; import io.cloudbeaver.service.auth.WebUserInfo; import io.cloudbeaver.service.security.SMUtils; import org.jkiss.code.NotNull; @@ -39,6 +41,7 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.auth.SMAuthInfo; import org.jkiss.dbeaver.model.auth.SMAuthStatus; +import org.jkiss.dbeaver.model.auth.SMSessionExternal; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.model.security.SMController; import org.jkiss.dbeaver.model.security.SMSubjectType; @@ -131,7 +134,7 @@ public WebAuthStatus authUpdateStatus(@NotNull WebSession webSession, @NotNull S } @Override - public void authLogout( + public WebLogoutInfo authLogout( @NotNull WebSession webSession, @Nullable String providerId, @Nullable String configurationId @@ -140,7 +143,26 @@ public void authLogout( throw new DBWebException("Not logged in"); } try { - webSession.removeAuthInfo(providerId); + List removedInfos = webSession.removeAuthInfo(providerId); + List logoutUrls = new ArrayList<>(); + var cbApp = CBApplication.getInstance(); + for (WebAuthInfo removedInfo : removedInfos) { + if (removedInfo.getAuthProviderDescriptor() + .getInstance() instanceof SMAuthProviderFederated federatedProvider + && removedInfo.getAuthSession() instanceof SMSessionExternal externalSession + ) { + var providerConfig = + cbApp.getAuthConfiguration().getAuthProviderConfiguration(removedInfo.getAuthConfiguration()); + if (providerConfig == null) { + log.warn(removedInfo.getAuthConfiguration() + " provider configuration wasn't found"); + continue; + } + String logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, + externalSession.getAuthParameters()); + logoutUrls.add(logoutUrl); + } + } + return new WebLogoutInfo(logoutUrls); } catch (DBException e) { throw new DBWebException("User logout failed", e); } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index e451dff1c0..c7ab132345 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -1348,7 +1348,8 @@ public SMAuthInfo authenticate( var authProviderFederated = (SMAuthProviderFederated) authProviderInstance; String signInLink = buildRedirectLink(authProviderFederated.getSignInLink(authProviderConfigurationId, Map.of()), authAttemptId); - String signOutLink = authProviderFederated.getSignOutLink(authProviderConfigurationId, Map.of()); + String signOutLink = authProviderFederated.getCommonSignOutLink(authProviderConfigurationId, + Map.of()); Map authData = Map.of(new SMAuthConfigurationReference(authProviderId, authProviderConfigurationId), filteredUserCreds); return SMAuthInfo.inProgress(authAttemptId, signInLink, signOutLink, authData, isMainSession); @@ -1621,9 +1622,12 @@ private SMAuthInfo getAuthStatus(@NotNull String authId, boolean readExpiredData signInLink = buildRedirectLink(((SMAuthProviderFederated) authProviderInstance).getRedirectLink( authProviderConfiguration, Map.of()), authId); - signOutLink = buildRedirectLink(((SMAuthProviderFederated) authProviderInstance).getSignOutLink( - authProviderConfiguration, - Map.of()), authId); + var userCustomSignOutLink = + ((SMAuthProviderFederated) authProviderInstance).getUserSignOutLink( + application.getAuthConfiguration() + .getAuthProviderConfiguration(authProviderConfiguration), + authProviderData); + signOutLink = userCustomSignOutLink; } } From 5585b077e80e8dea64b1529b3e7fc6f353b72b9f Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 12 Jan 2024 20:21:21 +0400 Subject: [PATCH 03/33] CB-4039 fix return value --- .../service/auth/WebServiceBindingAuth.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index 37d82cc772..6c7d3a418c 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -42,14 +42,11 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { env.getArgument("configuration"), env.getArgument("credentials"), CommonUtils.toBoolean(env.getArgument("linkUser")))) - .dataFetcher("authLogout", env -> { - getService(env).authLogout( - getWebSession(env, false), - env.getArgument("provider"), - env.getArgument("configuration") - ); - return true; - }) + .dataFetcher("authLogout", env -> getService(env).authLogout( + getWebSession(env, false), + env.getArgument("provider"), + env.getArgument("configuration") + )) .dataFetcher("authUpdateStatus", env -> getService(env).authUpdateStatus( getWebSession(env), env.getArgument("authId"), From 557910c777f7edf5ce7752279e09aecaa36ead75 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 15 Jan 2024 15:55:57 +0400 Subject: [PATCH 04/33] CB-4389. Added auto configuration for Reverse proxy if its enabled on ce, fixed typo, rename plugin parameters --- .../io.cloudbeaver.service.auth/plugin.xml | 3 + .../cloudbeaver/service/auth/RPConstants.java | 2 +- .../service/auth/RPSessionHandler.java | 2 + .../auth/ReverseProxyConfigurator.java | 88 +++++++++++++++++++ .../auth/ReverserProxyConfigurator.java | 26 ------ .../plugin.xml | 10 +-- .../auth/provider/rp/RPAuthProvider.java | 1 + 7 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java delete mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java diff --git a/server/bundles/io.cloudbeaver.service.auth/plugin.xml b/server/bundles/io.cloudbeaver.service.auth/plugin.xml index 01901218ac..e888ba9255 100644 --- a/server/bundles/io.cloudbeaver.service.auth/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.auth/plugin.xml @@ -7,6 +7,9 @@ + + diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index ab638eb311..c2e3c4b876 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,7 +1,7 @@ package io.cloudbeaver.service.auth; interface RPConstants { - String PARAM_LOGOUT_URL = "logout-url"; + String PARAM_LOGOUT_URL = "logout-url-header"; String PARAM_USER = "user-header"; String PARAM_TEAM = "team-header"; String PARAM_FIRST_NAME = "first-name-header"; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 8959e255ab..d0747379fe 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -94,6 +94,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); + String logoutUrl = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LOGOUT_URL), RPAuthProvider.X_LOGOUT_URL)); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -108,6 +109,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); + sessionParameters.put(SMConstants.SESSION_PARAM_LOGOUT_URL, logoutUrl); Map userCredentials = authProviderExternal.authExternalUser( webSession.getProgressMonitor(), null, credentials); String currentSmSessionId = webSession.getUser() == null ? null : webSession.getUserContext().getSmSessionId(); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java new file mode 100644 index 0000000000..e23aed90e0 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -0,0 +1,88 @@ +package io.cloudbeaver.service.auth; + +import io.cloudbeaver.auth.provider.rp.RPAuthProvider; +import io.cloudbeaver.model.app.WebAppConfiguration; +import io.cloudbeaver.model.app.WebApplication; +import io.cloudbeaver.model.app.WebAuthApplication; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.service.DBWServiceServerConfigurator; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; + +import java.util.HashMap; +import java.util.Map; + +public class ReverseProxyConfigurator implements DBWServiceServerConfigurator { + private static final Log log = Log.getLog(ReverseProxyConfigurator.class); + + @Override + public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { + + } + + @Override + public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { + if (migrationNotNeeded(application)) { + return; + } + migrateConfiguration(application); + } + + @Override + public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { + + } + + private void migrateConfiguration( + @NotNull WebApplication application + ) { + if (!(application instanceof WebAuthApplication authApplication)) { + return; + } + + SMAuthProviderCustomConfiguration smReverseProxyhProviderConfiguration = + authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); + boolean createNew = false; + if (smReverseProxyhProviderConfiguration == null) { + createNew = true; + smReverseProxyhProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyhProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyhProviderConfiguration.setDisplayName("Reverse Proxy"); + smReverseProxyhProviderConfiguration.setDescription( + "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" + ); + smReverseProxyhProviderConfiguration.setIconURL(""); + Map parameters = new HashMap<>(); + parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); + parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); + parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); + smReverseProxyhProviderConfiguration.setParameters(parameters); + } + + if (createNew) { + authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyhProviderConfiguration); + try { + authApplication.flushConfiguration(); + } catch (Exception e) { + log.error("Failed to save server configuration", e); + } + } + } + + private boolean migrationNotNeeded(@NotNull WebApplication application) { + if (!(application instanceof WebAuthApplication authApplication)) { + return true; + } + + if (!authApplication.getAuthConfiguration().isAuthProviderEnabled(RPAuthProvider.AUTH_PROVIDER)) { + log.debug("Reverse proxy provider disabled, migration not needed"); + return true; + } + return false; + } +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java deleted file mode 100644 index 567be05983..0000000000 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.cloudbeaver.service.auth; - -import io.cloudbeaver.model.app.WebAppConfiguration; -import io.cloudbeaver.model.app.WebApplication; -import io.cloudbeaver.model.session.WebSession; -import io.cloudbeaver.service.DBWServiceServerConfigurator; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.DBException; - -public class ReverserProxyConfigurator implements DBWServiceServerConfigurator { - @Override - public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { - - } - - @Override - public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { - DBWServiceServerConfigurator.super.migrateConfigurationIfNeeded(application); // - } - - @Override - public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { - - } -} diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index d992e5a5f4..35ec349ec6 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -28,12 +28,12 @@ - + - - - - + + + + diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 86de7f1c1b..8ca4c59010 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -48,6 +48,7 @@ public class RPAuthProvider implements SMAuthProviderExternal { public static final String X_ROLE_TE = "X-Role-TE"; public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; + public static final String X_LOGOUT_URL = "X-logout-url"; public static final String AUTH_PROVIDER = "reverseProxy"; @NotNull From 2c6cd7ad64011139ec19bcf7c608a7a9bb2b86ed Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 15 Jan 2024 16:29:52 +0400 Subject: [PATCH 05/33] CB-4389. Added check for already exist configuration --- .../service/auth/ReverseProxyConfigurator.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index e23aed90e0..f57f37de4c 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -19,8 +19,11 @@ public class ReverseProxyConfigurator implements DBWServiceServerConfigurator { private static final Log log = Log.getLog(ReverseProxyConfigurator.class); @Override - public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { - + public void configureServer( + @NotNull WebApplication application, + @Nullable WebSession session, + @NotNull WebAppConfiguration appConfig + ) throws DBException { } @Override @@ -83,6 +86,11 @@ private boolean migrationNotNeeded(@NotNull WebApplication application) { log.debug("Reverse proxy provider disabled, migration not needed"); return true; } + + if (authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER) != null) { + log.debug("Reverse proxy provider already exist, migration not needed"); + return true; + } return false; } } From 1712a6304775ebe4687c6bda5c8ed878002ac865 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 16 Jan 2024 15:09:46 +0400 Subject: [PATCH 06/33] CB-4389. Refactor after review --- .../auth/ReverseProxyConfigurator.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index f57f37de4c..23b7f40182 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -46,29 +46,24 @@ private void migrateConfiguration( return; } - SMAuthProviderCustomConfiguration smReverseProxyhProviderConfiguration = + SMAuthProviderCustomConfiguration smReverseProxyProviderConfiguration = authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); - boolean createNew = false; - if (smReverseProxyhProviderConfiguration == null) { - createNew = true; - smReverseProxyhProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyhProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyhProviderConfiguration.setDisplayName("Reverse Proxy"); - smReverseProxyhProviderConfiguration.setDescription( + if (smReverseProxyProviderConfiguration == null) { + smReverseProxyProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration .setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration .setDisplayName("Reverse Proxy"); + smReverseProxyProviderConfiguration .setDescription( "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" ); - smReverseProxyhProviderConfiguration.setIconURL(""); + smReverseProxyProviderConfiguration .setIconURL(""); Map parameters = new HashMap<>(); parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); - smReverseProxyhProviderConfiguration.setParameters(parameters); - } - - if (createNew) { - authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyhProviderConfiguration); + smReverseProxyProviderConfiguration .setParameters(parameters); + authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyProviderConfiguration ); try { authApplication.flushConfiguration(); } catch (Exception e) { @@ -87,7 +82,11 @@ private boolean migrationNotNeeded(@NotNull WebApplication application) { return true; } - if (authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER) != null) { + boolean isReverseProxyConfigured = authApplication.getAuthConfiguration() + .getAuthCustomConfigurations().stream() + .anyMatch(p -> p.getProvider().equals(RPAuthProvider.AUTH_PROVIDER)); + + if (isReverseProxyConfigured) { log.debug("Reverse proxy provider already exist, migration not needed"); return true; } From 5602f68d56e9cf62d510251abf3528f43c3420d0 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 16 Jan 2024 12:36:19 +0100 Subject: [PATCH 07/33] CB-4039 adds logout with context for Okta Identity Provider --- .../src/UserInfoResource.ts | 19 +++++++++++- .../src/queries/authentication/authLogout.gql | 14 ++++----- .../core-utils/src/getUniqueId.test.ts | 30 +++++++++++++++++++ webapp/packages/core-utils/src/getUniqueId.ts | 10 +++++++ webapp/packages/core-utils/src/index.ts | 1 + 5 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 webapp/packages/core-utils/src/getUniqueId.test.ts create mode 100644 webapp/packages/core-utils/src/getUniqueId.ts diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index aa268c7f70..b27f24ab1f 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -11,7 +11,9 @@ import { injectable } from '@cloudbeaver/core-di'; import { AutoRunningTask, ISyncExecutor, ITask, SyncExecutor, whileTask } from '@cloudbeaver/core-executor'; import { CachedDataResource, type ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource'; import { SessionDataResource, SessionResource } from '@cloudbeaver/core-root'; +import { WindowsService } from '@cloudbeaver/core-routing'; import { AuthInfo, AuthStatus, GetActiveUserQueryVariables, GraphQLService, UserInfo } from '@cloudbeaver/core-sdk'; +import { getUniqueId } from '@cloudbeaver/core-utils'; import { AUTH_PROVIDER_LOCAL_ID } from './AUTH_PROVIDER_LOCAL_ID'; import { AuthProviderService } from './AuthProviderService'; @@ -46,6 +48,7 @@ export class UserInfoResource extends CachedDataResource null, undefined, ['customIncludeOriginDetails', 'includeConfigurationParameters']); @@ -152,11 +155,25 @@ export class UserInfoResource extends CachedDataResource { - await this.graphQLService.sdk.authLogout({ + const { authLogout } = await this.graphQLService.sdk.authLogout({ provider, configuration, }); + // TODO handle all redirect links once we know what to do with multiple popups issue + const redirectLinks = authLogout?.redirectLinks || []; + + if (redirectLinks.length) { + const oktaLink = authLogout?.redirectLinks[0]; + const id = `okta-logout-id-${getUniqueId()}`; + + this.windowsService.open(id, { + url: oktaLink, + width: 400, + height: 400, + }); + } + this.resetIncludes(); this.setData(await this.loader()); this.sessionDataResource.markOutdated(); diff --git a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql index 86ec3b1489..cfb6ff30be 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql @@ -1,9 +1,5 @@ -query authLogout( - $provider: ID - $configuration: ID -) { - authLogout( - provider: $provider - configuration: $configuration - ) -} \ No newline at end of file +query authLogout($provider: ID, $configuration: ID) { + authLogout(provider: $provider, configuration: $configuration) { + redirectLinks + } +} diff --git a/webapp/packages/core-utils/src/getUniqueId.test.ts b/webapp/packages/core-utils/src/getUniqueId.test.ts new file mode 100644 index 0000000000..6265873278 --- /dev/null +++ b/webapp/packages/core-utils/src/getUniqueId.test.ts @@ -0,0 +1,30 @@ +import { getUniqueId } from './getUniqueId'; + +describe('getUniqueId', () => { + const uniqueIdsCount = 100; + + it('should return unique ids', () => { + const ids = new Set(); + for (let i = 0; i < uniqueIdsCount; i++) { + ids.add(getUniqueId()); + } + expect(ids.size).toBe(uniqueIdsCount); + }); + + it('should return unique ids in parallel', async () => { + const ids = new Set(); + const promises = []; + for (let i = 0; i < uniqueIdsCount; i++) { + promises.push( + new Promise(resolve => { + setTimeout(() => { + ids.add(getUniqueId()); + resolve(true); + }, Math.random() * 100); + }), + ); + } + await Promise.all(promises); + expect(ids.size).toBe(uniqueIdsCount); + }); +}); diff --git a/webapp/packages/core-utils/src/getUniqueId.ts b/webapp/packages/core-utils/src/getUniqueId.ts new file mode 100644 index 0000000000..f142d2297a --- /dev/null +++ b/webapp/packages/core-utils/src/getUniqueId.ts @@ -0,0 +1,10 @@ +function getNextIdGenerator() { + let id = 0; + return () => id++; +} + +const getNextId = getNextIdGenerator(); + +export function getUniqueId() { + return getNextId(); +} diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index b471a596a2..d3b1c0b94b 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -36,6 +36,7 @@ export * from './isPropertiesEqual'; export * from './isSafari'; export * from './isSameDay'; export * from './isValuesEqual'; +export * from './getUniqueId'; export * from './md5'; export * from './MetadataMap'; export * from './OrderedMap'; From 18be4247baeca5acd4aeb0ccff1a248987dc6fcb Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Tue, 16 Jan 2024 16:51:51 +0400 Subject: [PATCH 08/33] CB-4039 backward compatibility fix --- .../schema/service.auth.graphqls | 6 +++++- .../cloudbeaver/service/auth/WebServiceBindingAuth.java | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index b5e8db47fc..fa7b3b58af 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -143,7 +143,11 @@ extend type Query { authUpdateStatus(authId: ID!, linkUser: Boolean): AuthInfo! # Logouts user. If provider not specified then all authorizations are revoked from session. - authLogout(provider: ID, configuration: ID): LogoutInfo + @deprecated + authLogout(provider: ID, configuration: ID): Boolean! + + # Same as #authLogout, but returns additional information + authLogoutExtended(provider: ID, configuration: ID): LogoutInfo! # Active user information. null is no user was authorized within session activeUser: UserInfo diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index 6c7d3a418c..9ecc77b00a 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -42,11 +42,17 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { env.getArgument("configuration"), env.getArgument("credentials"), CommonUtils.toBoolean(env.getArgument("linkUser")))) - .dataFetcher("authLogout", env -> getService(env).authLogout( + .dataFetcher("authLogoutExtended", env -> getService(env).authLogout( getWebSession(env, false), env.getArgument("provider"), env.getArgument("configuration") )) + .dataFetcher("authLogout", env -> { + getService(env).authLogout(getWebSession(env, false), + env.getArgument("provider"), + env.getArgument("configuration")); + return true; + }) .dataFetcher("authUpdateStatus", env -> getService(env).authUpdateStatus( getWebSession(env), env.getArgument("authId"), From dfea9e73683d6c543ef35243e577f8f87a977c6c Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Tue, 16 Jan 2024 17:10:18 +0400 Subject: [PATCH 09/33] CB-4039 add since annotation --- .../io.cloudbeaver.service.auth/schema/service.auth.graphqls | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index fa7b3b58af..7601ffb332 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -86,7 +86,8 @@ type AuthInfo { userTokens: [UserAuthToken!] } -type LogoutInfo { + +type LogoutInfo @since(version: "23.3.3") { redirectLinks: [String!]! } @@ -147,6 +148,7 @@ extend type Query { authLogout(provider: ID, configuration: ID): Boolean! # Same as #authLogout, but returns additional information + @since(version: "23.3.3") authLogoutExtended(provider: ID, configuration: ID): LogoutInfo! # Active user information. null is no user was authorized within session From a547a29a2b8fd3175df4124f097e8e245fe83f04 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Tue, 16 Jan 2024 17:14:47 +0400 Subject: [PATCH 10/33] CB-4039 api return type fix --- .../io.cloudbeaver.service.auth/schema/service.auth.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index 7601ffb332..0f4fca656f 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -145,7 +145,7 @@ extend type Query { # Logouts user. If provider not specified then all authorizations are revoked from session. @deprecated - authLogout(provider: ID, configuration: ID): Boolean! + authLogout(provider: ID, configuration: ID): Boolean # Same as #authLogout, but returns additional information @since(version: "23.3.3") From 2d943a54772700a91f05107f5b5a2a4dcc547b41 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 16 Jan 2024 14:43:25 +0100 Subject: [PATCH 11/33] CB-4039 logout extended --- webapp/packages/core-authentication/src/UserInfoResource.ts | 6 +++--- .../core-sdk/src/queries/authentication/authLogout.gql | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index b27f24ab1f..74df6a7b46 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -155,16 +155,16 @@ export class UserInfoResource extends CachedDataResource { - const { authLogout } = await this.graphQLService.sdk.authLogout({ + const { authLogoutExtended } = await this.graphQLService.sdk.authLogout({ provider, configuration, }); // TODO handle all redirect links once we know what to do with multiple popups issue - const redirectLinks = authLogout?.redirectLinks || []; + const redirectLinks = authLogoutExtended?.redirectLinks || []; if (redirectLinks.length) { - const oktaLink = authLogout?.redirectLinks[0]; + const oktaLink = authLogoutExtended?.redirectLinks[0]; const id = `okta-logout-id-${getUniqueId()}`; this.windowsService.open(id, { diff --git a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql index cfb6ff30be..9420c4e5bf 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql @@ -1,5 +1,5 @@ query authLogout($provider: ID, $configuration: ID) { - authLogout(provider: $provider, configuration: $configuration) { + authLogoutExtended(provider: $provider, configuration: $configuration) { redirectLinks } } From 1a174477b2fe8f0677da6affefd39fb51f6c9995 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 16 Jan 2024 19:57:14 +0400 Subject: [PATCH 12/33] CB-4389. Refactor after review --- .../src/io/cloudbeaver/service/auth/RPConstants.java | 2 +- .../io/cloudbeaver/service/auth/RPSessionHandler.java | 11 ++++++----- .../service/auth/ReverseProxyConfigurator.java | 11 +++++------ .../io.cloudbeaver.service.security/plugin.xml | 2 +- .../cloudbeaver/auth/provider/rp/RPAuthProvider.java | 1 - 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index c2e3c4b876..ab638eb311 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,7 +1,7 @@ package io.cloudbeaver.service.auth; interface RPConstants { - String PARAM_LOGOUT_URL = "logout-url-header"; + String PARAM_LOGOUT_URL = "logout-url"; String PARAM_USER = "user-header"; String PARAM_TEAM = "team-header"; String PARAM_FIRST_NAME = "first-name-header"; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index d0747379fe..3e1e82b485 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -40,10 +40,8 @@ import java.io.IOException; import java.text.MessageFormat; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -94,7 +92,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); - String logoutUrl = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LOGOUT_URL), RPAuthProvider.X_LOGOUT_URL)); + String logoutUrl = Objects.requireNonNull(configuration).getParameter(RPConstants.PARAM_LOGOUT_URL); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -106,6 +104,9 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not if (!CommonUtils.isEmpty(lastName)) { credentials.put(SMStandardMeta.META_LAST_NAME, lastName); } + if (!CommonUtils.isNotEmpty(logoutUrl)) { + credentials.put("logoutUrl", logoutUrl); + } Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index 23b7f40182..ad914fc43e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -50,19 +50,18 @@ private void migrateConfiguration( authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); if (smReverseProxyProviderConfiguration == null) { smReverseProxyProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyProviderConfiguration .setProvider(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyProviderConfiguration .setDisplayName("Reverse Proxy"); - smReverseProxyProviderConfiguration .setDescription( + smReverseProxyProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration.setDisplayName("Reverse Proxy"); + smReverseProxyProviderConfiguration.setDescription( "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" ); smReverseProxyProviderConfiguration .setIconURL(""); Map parameters = new HashMap<>(); parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); - parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); - parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_TEAM); parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); - smReverseProxyProviderConfiguration .setParameters(parameters); + smReverseProxyProviderConfiguration.setParameters(parameters); authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyProviderConfiguration ); try { authApplication.flushConfiguration(); diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index 35ec349ec6..5878dca3c7 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -28,7 +28,7 @@ - + diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 8ca4c59010..86de7f1c1b 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -48,7 +48,6 @@ public class RPAuthProvider implements SMAuthProviderExternal { public static final String X_ROLE_TE = "X-Role-TE"; public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; - public static final String X_LOGOUT_URL = "X-logout-url"; public static final String AUTH_PROVIDER = "reverseProxy"; @NotNull From ed6638265610ceb52b95f9db841feec95d3d112e Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Wed, 17 Jan 2024 14:34:00 +0400 Subject: [PATCH 13/33] CB-4389. Refactor after review --- .../auth/SMAuthProviderFederated.java | 6 +---- .../auth/SMSignOutLinkProvider.java | 27 +++++++++++++++++++ .../WebAuthProviderConfiguration.java | 5 ++-- .../cloudbeaver/service/auth/RPConstants.java | 16 +++++++++++ .../service/auth/RPSessionHandler.java | 2 +- .../auth/ReverseProxyConfigurator.java | 16 +++++++++++ .../auth/provider/rp/RPAuthProvider.java | 13 ++++++++- .../CBEmbeddedSecurityController.java | 2 +- 8 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java index 1747122729..e7e700e479 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java @@ -26,7 +26,7 @@ * Federated auth provider. * Provides links to external auth resource */ -public interface SMAuthProviderFederated { +public interface SMAuthProviderFederated extends SMSignOutLinkProvider { /** * Returns new identifying credentials which can be used to find/create user in database @@ -34,10 +34,6 @@ public interface SMAuthProviderFederated { @NotNull String getSignInLink(String id, @NotNull Map providerConfig) throws DBException; - - @NotNull - String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; - @Nullable String getMetadataLink(String id, @NotNull Map providerConfig) throws DBException; diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java new file mode 100644 index 0000000000..583e1bc1d7 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java @@ -0,0 +1,27 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ +package io.cloudbeaver.auth; + +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; + +import java.util.Map; + +public interface SMSignOutLinkProvider { + @NotNull + String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java index 266382b8cc..d79b708ed0 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java @@ -18,6 +18,7 @@ import io.cloudbeaver.auth.CBAuthConstants; import io.cloudbeaver.auth.SMAuthProviderFederated; +import io.cloudbeaver.auth.SMSignOutLinkProvider; import io.cloudbeaver.utils.WebAppUtils; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; @@ -85,8 +86,8 @@ private String buildRedirectUrl(String baseUrl) { @Property public String getSignOutLink() throws DBException { SMAuthProvider instance = providerDescriptor.getInstance(); - return instance instanceof SMAuthProviderFederated - ? ((SMAuthProviderFederated) instance).getSignOutLink(getId(), config.getParameters()) + return instance instanceof SMSignOutLinkProvider + ? ((SMSignOutLinkProvider) instance).getSignOutLink(getId(), config.getParameters()) : null; } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index ab638eb311..304be1493a 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,3 +1,19 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ package io.cloudbeaver.service.auth; interface RPConstants { diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 89ccb89021..614cf8a9d5 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -123,7 +123,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not webSession.getSessionId(), currentSmSessionId, sessionParameters, - WebSession.CB_SESSION_TYPE, authProvider.getId(), null, userCredentials); + WebSession.CB_SESSION_TYPE, authProvider.getId(), configuration.getId(), userCredentials); new WebSessionAuthProcessor(webSession, smAuthInfo, false).authenticateSession(); log.debug(MessageFormat.format( "Successful reverse proxy authentication: user ''{0}'' with teams {1}", userName, userTeams)); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index ad914fc43e..c0e337b4b4 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -1,3 +1,19 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ package io.cloudbeaver.service.auth; import io.cloudbeaver.auth.provider.rp.RPAuthProvider; diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 8d8d0a0244..813a9aa928 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -18,14 +18,18 @@ import io.cloudbeaver.DBWUserIdentity; import io.cloudbeaver.auth.SMAuthProviderExternal; +import io.cloudbeaver.auth.SMAuthProviderFederated; +import io.cloudbeaver.auth.SMSignOutLinkProvider; import io.cloudbeaver.auth.provider.local.LocalAuthSession; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebUser; +import io.cloudbeaver.utils.WebAppUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPObject; +import org.jkiss.dbeaver.model.auth.SMAuthProvider; import org.jkiss.dbeaver.model.auth.SMSession; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; @@ -37,7 +41,7 @@ import java.util.HashMap; import java.util.Map; -public class RPAuthProvider implements SMAuthProviderExternal { +public class RPAuthProvider implements SMAuthProviderExternal, SMSignOutLinkProvider { private static final Log log = Log.getLog(RPAuthProvider.class); @@ -122,4 +126,11 @@ public void closeSession(@NotNull SMSession mainSession, SMSession session) thro public void refreshSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, SMSession session) throws DBException { } + + @NotNull + @Override + public String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException { + return providerConfig.get("logout-url").toString(); + } + } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index e451dff1c0..6f9a1cc912 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -1348,7 +1348,7 @@ public SMAuthInfo authenticate( var authProviderFederated = (SMAuthProviderFederated) authProviderInstance; String signInLink = buildRedirectLink(authProviderFederated.getSignInLink(authProviderConfigurationId, Map.of()), authAttemptId); - String signOutLink = authProviderFederated.getSignOutLink(authProviderConfigurationId, Map.of()); + String signOutLink = authProviderFederated.getSignOutLink(authProviderConfigurationId, providerConfig.getParameters()); Map authData = Map.of(new SMAuthConfigurationReference(authProviderId, authProviderConfigurationId), filteredUserCreds); return SMAuthInfo.inProgress(authAttemptId, signInLink, signOutLink, authData, isMainSession); From 38341afed594e0b671564133bd9ee14d9b4290ba Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 17 Jan 2024 11:41:21 +0100 Subject: [PATCH 14/33] CB-4039 okta logout redirect moved to AuthenticationService --- .../src/UserInfoResource.ts | 23 ++++---------- .../core-utils/src/getUniqueId.test.ts | 30 ------------------- webapp/packages/core-utils/src/getUniqueId.ts | 10 ------- webapp/packages/core-utils/src/index.ts | 1 - .../src/AuthenticationService.ts | 17 ++++++++++- 5 files changed, 21 insertions(+), 60 deletions(-) delete mode 100644 webapp/packages/core-utils/src/getUniqueId.test.ts delete mode 100644 webapp/packages/core-utils/src/getUniqueId.ts diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index 74df6a7b46..31624b32a6 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -12,8 +12,7 @@ import { AutoRunningTask, ISyncExecutor, ITask, SyncExecutor, whileTask } from ' import { CachedDataResource, type ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource'; import { SessionDataResource, SessionResource } from '@cloudbeaver/core-root'; import { WindowsService } from '@cloudbeaver/core-routing'; -import { AuthInfo, AuthStatus, GetActiveUserQueryVariables, GraphQLService, UserInfo } from '@cloudbeaver/core-sdk'; -import { getUniqueId } from '@cloudbeaver/core-utils'; +import { AuthInfo, AuthLogoutQuery, AuthStatus, GetActiveUserQueryVariables, GraphQLService, UserInfo } from '@cloudbeaver/core-sdk'; import { AUTH_PROVIDER_LOCAL_ID } from './AUTH_PROVIDER_LOCAL_ID'; import { AuthProviderService } from './AuthProviderService'; @@ -154,29 +153,17 @@ export class UserInfoResource extends CachedDataResource { - const { authLogoutExtended } = await this.graphQLService.sdk.authLogout({ + async logout(provider?: string, configuration?: string): Promise { + const result = await this.graphQLService.sdk.authLogout({ provider, configuration, }); - // TODO handle all redirect links once we know what to do with multiple popups issue - const redirectLinks = authLogoutExtended?.redirectLinks || []; - - if (redirectLinks.length) { - const oktaLink = authLogoutExtended?.redirectLinks[0]; - const id = `okta-logout-id-${getUniqueId()}`; - - this.windowsService.open(id, { - url: oktaLink, - width: 400, - height: 400, - }); - } - this.resetIncludes(); this.setData(await this.loader()); this.sessionDataResource.markOutdated(); + + return result; } async setConfigurationParameter(key: string, value: any): Promise { diff --git a/webapp/packages/core-utils/src/getUniqueId.test.ts b/webapp/packages/core-utils/src/getUniqueId.test.ts deleted file mode 100644 index 6265873278..0000000000 --- a/webapp/packages/core-utils/src/getUniqueId.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getUniqueId } from './getUniqueId'; - -describe('getUniqueId', () => { - const uniqueIdsCount = 100; - - it('should return unique ids', () => { - const ids = new Set(); - for (let i = 0; i < uniqueIdsCount; i++) { - ids.add(getUniqueId()); - } - expect(ids.size).toBe(uniqueIdsCount); - }); - - it('should return unique ids in parallel', async () => { - const ids = new Set(); - const promises = []; - for (let i = 0; i < uniqueIdsCount; i++) { - promises.push( - new Promise(resolve => { - setTimeout(() => { - ids.add(getUniqueId()); - resolve(true); - }, Math.random() * 100); - }), - ); - } - await Promise.all(promises); - expect(ids.size).toBe(uniqueIdsCount); - }); -}); diff --git a/webapp/packages/core-utils/src/getUniqueId.ts b/webapp/packages/core-utils/src/getUniqueId.ts deleted file mode 100644 index f142d2297a..0000000000 --- a/webapp/packages/core-utils/src/getUniqueId.ts +++ /dev/null @@ -1,10 +0,0 @@ -function getNextIdGenerator() { - let id = 0; - return () => id++; -} - -const getNextId = getNextIdGenerator(); - -export function getUniqueId() { - return getNextId(); -} diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index d3b1c0b94b..b471a596a2 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -36,7 +36,6 @@ export * from './isPropertiesEqual'; export * from './isSafari'; export * from './isSameDay'; export * from './isValuesEqual'; -export * from './getUniqueId'; export * from './md5'; export * from './MetadataMap'; export * from './OrderedMap'; diff --git a/webapp/packages/plugin-authentication/src/AuthenticationService.ts b/webapp/packages/plugin-authentication/src/AuthenticationService.ts index c22e53f9b6..ceeb541891 100644 --- a/webapp/packages/plugin-authentication/src/AuthenticationService.ts +++ b/webapp/packages/plugin-authentication/src/AuthenticationService.ts @@ -27,6 +27,7 @@ import { CachedMapAllKey } from '@cloudbeaver/core-resource'; import { ISessionAction, ServerConfigResource, sessionActionContext, SessionActionService, SessionDataResource } from '@cloudbeaver/core-root'; import { ScreenService, WindowsService } from '@cloudbeaver/core-routing'; import { NavigationService } from '@cloudbeaver/core-ui'; +import { uuid } from '@cloudbeaver/core-utils'; import { AuthDialogService } from './Dialog/AuthDialogService'; import type { IAuthOptions } from './IAuthOptions'; @@ -106,7 +107,21 @@ export class AuthenticationService extends Bootstrap { } try { - await this.userInfoResource.logout(providerId, configurationId); + // TODO handle all redirect links once we know what to do with multiple popups issue + const { + authLogoutExtended: { redirectLinks }, + } = await this.userInfoResource.logout(providerId, configurationId); + + if (redirectLinks.length) { + const oktaLink = redirectLinks[0]; + const id = `okta-logout-id-${uuid()}`; + + this.windowsService.open(id, { + url: oktaLink, + width: 400, + height: 400, + }); + } if (!this.administrationScreenService.isConfigurationMode && !providerId) { this.screenService.navigateToRoot(); From b08cfa1eb4c81d79c7fdf84b96842a8f713f3455 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 17 Jan 2024 11:43:04 +0100 Subject: [PATCH 15/33] CB-4039 remove windowService dep --- webapp/packages/core-authentication/src/UserInfoResource.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index 31624b32a6..99c0b115a5 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -11,7 +11,6 @@ import { injectable } from '@cloudbeaver/core-di'; import { AutoRunningTask, ISyncExecutor, ITask, SyncExecutor, whileTask } from '@cloudbeaver/core-executor'; import { CachedDataResource, type ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource'; import { SessionDataResource, SessionResource } from '@cloudbeaver/core-root'; -import { WindowsService } from '@cloudbeaver/core-routing'; import { AuthInfo, AuthLogoutQuery, AuthStatus, GetActiveUserQueryVariables, GraphQLService, UserInfo } from '@cloudbeaver/core-sdk'; import { AUTH_PROVIDER_LOCAL_ID } from './AUTH_PROVIDER_LOCAL_ID'; @@ -47,7 +46,6 @@ export class UserInfoResource extends CachedDataResource null, undefined, ['customIncludeOriginDetails', 'includeConfigurationParameters']); From e20314eef19a72dd84ed23da0d75335b6ba22884 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 17 Jan 2024 14:43:01 +0100 Subject: [PATCH 16/33] CB-4039 removes old logic of identity provider logout links --- .../src/AuthInfoService.ts | 25 ------------ .../src/AuthenticationService.ts | 40 ++----------------- .../plugin-authentication/src/locales/en.ts | 1 + .../plugin-authentication/src/locales/it.ts | 1 + .../plugin-authentication/src/locales/ru.ts | 1 + .../plugin-authentication/src/locales/zh.ts | 1 + 6 files changed, 7 insertions(+), 62 deletions(-) diff --git a/webapp/packages/core-authentication/src/AuthInfoService.ts b/webapp/packages/core-authentication/src/AuthInfoService.ts index 23ba0d8d66..ca8b6c48c2 100644 --- a/webapp/packages/core-authentication/src/AuthInfoService.ts +++ b/webapp/packages/core-authentication/src/AuthInfoService.ts @@ -25,31 +25,6 @@ export class AuthInfoService { return this.userInfoResource.data; } - get userAuthConfigurations(): IUserAuthConfiguration[] { - const tokens = this.userInfo?.authTokens; - const result: IUserAuthConfiguration[] = []; - - if (!tokens) { - return result; - } - - for (const token of tokens) { - if (token.authConfiguration) { - const provider = this.authProvidersResource.values.find(provider => provider.id === token.authProvider); - - if (provider) { - const configuration = provider.configurations?.find(configuration => configuration.id === token.authConfiguration); - - if (configuration) { - result.push({ providerId: provider.id, configuration }); - } - } - } - } - - return result; - } - constructor( private readonly userInfoResource: UserInfoResource, private readonly authProvidersResource: AuthProvidersResource, diff --git a/webapp/packages/plugin-authentication/src/AuthenticationService.ts b/webapp/packages/plugin-authentication/src/AuthenticationService.ts index ceeb541891..5a596e6a05 100644 --- a/webapp/packages/plugin-authentication/src/AuthenticationService.ts +++ b/webapp/packages/plugin-authentication/src/AuthenticationService.ts @@ -11,11 +11,9 @@ import { AdministrationScreenService } from '@cloudbeaver/core-administration'; import { AppAuthService, AUTH_PROVIDER_LOCAL_ID, - AuthInfoService, AuthProviderContext, AuthProviderService, AuthProvidersResource, - IUserAuthConfiguration, RequestedProvider, UserInfoResource, } from '@cloudbeaver/core-authentication'; @@ -55,7 +53,6 @@ export class AuthenticationService extends Bootstrap { private readonly authProviderService: AuthProviderService, private readonly authProvidersResource: AuthProvidersResource, private readonly sessionDataResource: SessionDataResource, - private readonly authInfoService: AuthInfoService, private readonly serverConfigResource: ServerConfigResource, private readonly windowsService: WindowsService, private readonly sessionActionService: SessionActionService, @@ -92,20 +89,6 @@ export class AuthenticationService extends Bootstrap { return; } - let userAuthConfiguration: IUserAuthConfiguration | undefined = undefined; - - if (providerId) { - userAuthConfiguration = this.authInfoService.userAuthConfigurations.find( - c => c.providerId === providerId && c.configuration.id === configurationId, - ); - } else if (this.authInfoService.userAuthConfigurations.length > 0) { - userAuthConfiguration = this.authInfoService.userAuthConfigurations[0]; - } - - if (userAuthConfiguration?.configuration.signOutLink) { - this.logoutConfiguration(userAuthConfiguration); - } - try { // TODO handle all redirect links once we know what to do with multiple popups issue const { @@ -113,11 +96,11 @@ export class AuthenticationService extends Bootstrap { } = await this.userInfoResource.logout(providerId, configurationId); if (redirectLinks.length) { - const oktaLink = redirectLinks[0]; + const url = redirectLinks[0]; const id = `okta-logout-id-${uuid()}`; this.windowsService.open(id, { - url: oktaLink, + url, width: 400, height: 400, }); @@ -129,24 +112,7 @@ export class AuthenticationService extends Bootstrap { await this.onLogout.execute('after'); } catch (exception: any) { - this.notificationService.logException(exception, "Can't logout"); - } - } - - private async logoutConfiguration(configuration: IUserAuthConfiguration): Promise { - if (configuration.configuration.signOutLink) { - const id = `${configuration.configuration.id}-sign-out`; - const popup = this.windowsService.open(id, { - url: configuration.configuration.signOutLink, - target: id, - width: 600, - height: 700, - }); - - if (popup) { - popup.blur(); - window.focus(); - } + this.notificationService.logException(exception, 'authentication_logout_error'); } } diff --git a/webapp/packages/plugin-authentication/src/locales/en.ts b/webapp/packages/plugin-authentication/src/locales/en.ts index 935fdc3318..a1c94e382f 100644 --- a/webapp/packages/plugin-authentication/src/locales/en.ts +++ b/webapp/packages/plugin-authentication/src/locales/en.ts @@ -2,6 +2,7 @@ export default [ ['authentication_login_dialog_title', 'Authentication'], ['authentication_login', 'Login'], ['authentication_logout', 'Logout'], + ['authentication_logout_error', "Can't logout"], ['authentication_authenticate', 'Authenticate'], ['authentication_authorizing', 'Authorizing...'], ['authentication_auth_federated', 'Federated'], diff --git a/webapp/packages/plugin-authentication/src/locales/it.ts b/webapp/packages/plugin-authentication/src/locales/it.ts index 00115b741c..d3be9e9de5 100644 --- a/webapp/packages/plugin-authentication/src/locales/it.ts +++ b/webapp/packages/plugin-authentication/src/locales/it.ts @@ -2,6 +2,7 @@ export default [ ['authentication_login_dialog_title', 'Autenticazione'], ['authentication_login', 'Login'], ['authentication_logout', 'Logout'], + ['authentication_logout_error', "Can't logout"], ['authentication_authenticate', 'Autentica'], ['authentication_authorizing', 'Authorizing...'], ['authentication_auth_federated', 'Federated'], diff --git a/webapp/packages/plugin-authentication/src/locales/ru.ts b/webapp/packages/plugin-authentication/src/locales/ru.ts index 228e105af9..576bb05cd1 100644 --- a/webapp/packages/plugin-authentication/src/locales/ru.ts +++ b/webapp/packages/plugin-authentication/src/locales/ru.ts @@ -2,6 +2,7 @@ export default [ ['authentication_login_dialog_title', 'Аутентификация'], ['authentication_login', 'Войти'], ['authentication_logout', 'Выйти'], + ['authentication_logout_error', 'Не удалось выйти'], ['authentication_authenticate', 'Аутентифицироваться'], ['authentication_authorizing', 'Авторизация...'], ['authentication_auth_federated', 'Федеративная'], diff --git a/webapp/packages/plugin-authentication/src/locales/zh.ts b/webapp/packages/plugin-authentication/src/locales/zh.ts index 97476a707d..4af3f890b7 100644 --- a/webapp/packages/plugin-authentication/src/locales/zh.ts +++ b/webapp/packages/plugin-authentication/src/locales/zh.ts @@ -2,6 +2,7 @@ export default [ ['authentication_login_dialog_title', '认证'], ['authentication_login', '登录'], ['authentication_logout', '登出'], + ['authentication_logout_error', "Can't logout"], ['authentication_authenticate', '认证'], ['authentication_authorizing', 'Authorizing...'], ['authentication_auth_federated', '联合认证'], From 3e4587c3f3494b03a901078fae0a225a0df3e68d Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 17 Jan 2024 16:38:56 +0100 Subject: [PATCH 17/33] CB-4039 okta review changes --- .../src/UserInfoResource.ts | 2 + .../src/queries/authentication/authLogout.gql | 2 +- .../src/AuthenticationService.ts | 41 ++++++++++++------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index 99c0b115a5..e11390d484 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -20,6 +20,8 @@ import type { IAuthCredentials } from './IAuthCredentials'; export type UserInfoIncludes = GetActiveUserQueryVariables; +export type UserLogoutInfo = AuthLogoutQuery['result']; + export interface ILoginOptions { credentials?: IAuthCredentials; configurationId?: string; diff --git a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql index 9420c4e5bf..4997dbbd56 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/authLogout.gql @@ -1,5 +1,5 @@ query authLogout($provider: ID, $configuration: ID) { - authLogoutExtended(provider: $provider, configuration: $configuration) { + result: authLogoutExtended(provider: $provider, configuration: $configuration) { redirectLinks } } diff --git a/webapp/packages/plugin-authentication/src/AuthenticationService.ts b/webapp/packages/plugin-authentication/src/AuthenticationService.ts index 5a596e6a05..91ec32a7f3 100644 --- a/webapp/packages/plugin-authentication/src/AuthenticationService.ts +++ b/webapp/packages/plugin-authentication/src/AuthenticationService.ts @@ -16,6 +16,7 @@ import { AuthProvidersResource, RequestedProvider, UserInfoResource, + UserLogoutInfo, } from '@cloudbeaver/core-authentication'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import type { DialogueStateResult } from '@cloudbeaver/core-dialogs'; @@ -90,21 +91,9 @@ export class AuthenticationService extends Bootstrap { } try { - // TODO handle all redirect links once we know what to do with multiple popups issue - const { - authLogoutExtended: { redirectLinks }, - } = await this.userInfoResource.logout(providerId, configurationId); - - if (redirectLinks.length) { - const url = redirectLinks[0]; - const id = `okta-logout-id-${uuid()}`; - - this.windowsService.open(id, { - url, - width: 400, - height: 400, - }); - } + const logoutResult = await this.userInfoResource.logout(providerId, configurationId); + + this.handleRedirectLinks(logoutResult.result); if (!this.administrationScreenService.isConfigurationMode && !providerId) { this.screenService.navigateToRoot(); @@ -116,6 +105,28 @@ export class AuthenticationService extends Bootstrap { } } + // TODO handle all redirect links once we know what to do with multiple popups issue + private handleRedirectLinks(userLogoutInfo: UserLogoutInfo) { + const redirectLinks = userLogoutInfo.redirectLinks; + + if (redirectLinks.length) { + const url = redirectLinks[0]; + const id = `okta-logout-id-${uuid()}`; + + const popup = this.windowsService.open(id, { + url, + target: id, + width: 600, + height: 700, + }); + + if (popup) { + popup.blur(); + window.focus(); + } + } + } + private async auth(persistent: boolean, options: IAuthOptions) { if (this.authPromise) { await this.waitAuth(); From 83292a28038736c14dda87eca9ed056dad37432c Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Wed, 17 Jan 2024 21:23:50 +0400 Subject: [PATCH 18/33] CB-4389. Merge logout logic into branch --- .../io/cloudbeaver/auth/SMAuthProviderFederated.java | 7 +------ .../io/cloudbeaver/auth/SMSignOutLinkProvider.java | 12 +++++++++++- .../cloudbeaver/auth/provider/rp/RPAuthProvider.java | 10 ++++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java index 3da6adaf45..bd2bab25e2 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java @@ -32,12 +32,7 @@ public interface SMAuthProviderFederated extends SMSignOutLinkProvider { @NotNull String getSignInLink(String id, @NotNull Map providerConfig) throws DBException; - /** - * @return a common link for logout, not related with the user context - */ - @NotNull - String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException; - + @Override default String getUserSignOutLink( @NotNull SMAuthProviderCustomConfiguration providerConfig, @NotNull Map userCredentials diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java index 583e1bc1d7..3cbd7f70b1 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java @@ -18,10 +18,20 @@ import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import java.util.Map; public interface SMSignOutLinkProvider { + + /** + * @return a common link for logout, not related with the user context + */ @NotNull - String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + + String getUserSignOutLink( + @NotNull SMAuthProviderCustomConfiguration providerConfig, + @NotNull Map userCredentials + ) throws DBException; } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 813a9aa928..0a9c736f27 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -53,6 +53,7 @@ public class RPAuthProvider implements SMAuthProviderExternal, SMSign public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; public static final String AUTH_PROVIDER = "reverseProxy"; + public static final String LOGOUT_URL = "logout-url"; @NotNull @Override @@ -129,8 +130,13 @@ public void refreshSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSessi @NotNull @Override - public String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException { - return providerConfig.get("logout-url").toString(); + public String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException { + return providerConfig.get(LOGOUT_URL).toString(); + } + + @Override + public String getUserSignOutLink(@NotNull SMAuthProviderCustomConfiguration providerConfig, @NotNull Map userCredentials) throws DBException { + return providerConfig.getParameters().get(LOGOUT_URL).toString(); } } From 6af4e79aa753917e86d907ecc16662f61b6054c2 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 22 Jan 2024 14:11:22 +0400 Subject: [PATCH 19/33] CB-4389. Delete param after review --- .../src/io/cloudbeaver/service/auth/RPSessionHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 614cf8a9d5..021e509678 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -112,7 +112,6 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); - sessionParameters.put(SMConstants.SESSION_PARAM_LOGOUT_URL, logoutUrl); Map userCredentials = authProviderExternal.authExternalUser( webSession.getProgressMonitor(), null, credentials); String currentSmSessionId = webSession.getUser() == null ? null : webSession.getUserContext().getSmSessionId(); From 48eece40de7cd372243269afcc851bccf6ad3193 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Thu, 11 Jan 2024 21:37:53 +0400 Subject: [PATCH 20/33] CB-4389. Converted reverse proxy to identity provider --- .../cloudbeaver/service/auth/RPConstants.java | 10 ++++++ .../service/auth/RPSessionHandler.java | 35 +++++++++++++++---- .../auth/ReverserProxyConfigurator.java | 26 ++++++++++++++ .../plugin.xml | 11 ++++++ 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java new file mode 100644 index 0000000000..ab638eb311 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -0,0 +1,10 @@ +package io.cloudbeaver.service.auth; + +interface RPConstants { + String PARAM_LOGOUT_URL = "logout-url"; + String PARAM_USER = "user-header"; + String PARAM_TEAM = "team-header"; + String PARAM_FIRST_NAME = "first-name-header"; + String PARAM_LAST_NAME = "last-name-header"; + String PARAM_ROLE_NAME = "role-header"; +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 10f4669cb6..14356a1fbc 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -33,6 +33,7 @@ import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.auth.SMAuthInfo; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import org.jkiss.dbeaver.model.security.SMConstants; import org.jkiss.dbeaver.model.security.SMController; import org.jkiss.dbeaver.model.security.SMStandardMeta; @@ -62,22 +63,37 @@ public boolean handleSessionOpen(WebSession webSession, HttpServletRequest reque return false; } - public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @NotNull WebSession webSession) throws DBWebException { + public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @NotNull WebSession webSession) throws DBException { SMController securityController = webSession.getSecurityController(); WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(RPAuthProvider.AUTH_PROVIDER); if (authProvider == null) { throw new DBWebException("Auth provider " + RPAuthProvider.AUTH_PROVIDER + " not found"); } SMAuthProviderExternal authProviderExternal = (SMAuthProviderExternal) authProvider.getInstance(); - String userName = request.getHeader(RPAuthProvider.X_USER); - String teams = request.getHeader(RPAuthProvider.X_TEAM); + SMAuthProviderCustomConfiguration configuration = WebAppUtils.getWebAuthApplication() + .getAuthConfiguration() + .getAuthCustomConfigurations() + .stream() + .filter(p -> p.getProvider().equals(authProvider.toString())) + .findFirst() + .orElse(null); + Map paramConfigMap = new HashMap<>(); + if (configuration != null) { + authProvider.getConfigurationParameters().forEach(p -> + paramConfigMap.put(p.getId(), configuration.getParameters().get(p.getId()) + )); + } + String userName = request.getHeader( + resolveParam(paramConfigMap.get(RPConstants.PARAM_USER), RPAuthProvider.X_USER) + ); + String teams = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_TEAM), RPAuthProvider.X_TEAM)); if (CommonUtils.isEmpty(teams)) { // backward compatibility teams = request.getHeader(RPAuthProvider.X_ROLE); } - String role = request.getHeader(RPAuthProvider.X_ROLE_TE); - String firstName = request.getHeader(RPAuthProvider.X_FIRST_NAME); - String lastName = request.getHeader(RPAuthProvider.X_LAST_NAME); + String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); + String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); + String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -120,4 +136,11 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not public boolean handleSessionClose(WebSession webSession) throws DBException, IOException { return false; } + + private String resolveParam(Object value, String defaultValue) { + if (value != null && !value.toString().isEmpty()) { + return value.toString(); + } + return defaultValue; + } } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java new file mode 100644 index 0000000000..567be05983 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java @@ -0,0 +1,26 @@ +package io.cloudbeaver.service.auth; + +import io.cloudbeaver.model.app.WebAppConfiguration; +import io.cloudbeaver.model.app.WebApplication; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.service.DBWServiceServerConfigurator; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; + +public class ReverserProxyConfigurator implements DBWServiceServerConfigurator { + @Override + public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { + + } + + @Override + public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { + DBWServiceServerConfigurator.super.migrateConfigurationIfNeeded(application); // + } + + @Override + public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { + + } +} diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index 88eff847ae..d992e5a5f4 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -18,6 +18,7 @@ @@ -25,6 +26,16 @@ + + + + + + + + + + From 3a8034abff82536cadaf38c407b14eee9760bf38 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 15 Jan 2024 15:55:57 +0400 Subject: [PATCH 21/33] CB-4389. Added auto configuration for Reverse proxy if its enabled on ce, fixed typo, rename plugin parameters --- .../io.cloudbeaver.service.auth/plugin.xml | 3 + .../cloudbeaver/service/auth/RPConstants.java | 2 +- .../service/auth/RPSessionHandler.java | 2 + .../auth/ReverseProxyConfigurator.java | 88 +++++++++++++++++++ .../auth/ReverserProxyConfigurator.java | 26 ------ .../plugin.xml | 10 +-- .../auth/provider/rp/RPAuthProvider.java | 1 + 7 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java delete mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java diff --git a/server/bundles/io.cloudbeaver.service.auth/plugin.xml b/server/bundles/io.cloudbeaver.service.auth/plugin.xml index 01901218ac..e888ba9255 100644 --- a/server/bundles/io.cloudbeaver.service.auth/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.auth/plugin.xml @@ -7,6 +7,9 @@ + + diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index ab638eb311..c2e3c4b876 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,7 +1,7 @@ package io.cloudbeaver.service.auth; interface RPConstants { - String PARAM_LOGOUT_URL = "logout-url"; + String PARAM_LOGOUT_URL = "logout-url-header"; String PARAM_USER = "user-header"; String PARAM_TEAM = "team-header"; String PARAM_FIRST_NAME = "first-name-header"; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 14356a1fbc..c6f05c8949 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -94,6 +94,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); + String logoutUrl = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LOGOUT_URL), RPAuthProvider.X_LOGOUT_URL)); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -108,6 +109,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); + sessionParameters.put(SMConstants.SESSION_PARAM_LOGOUT_URL, logoutUrl); Map userCredentials = authProviderExternal.authExternalUser( webSession.getProgressMonitor(), null, credentials); String currentSmSessionId = webSession.getUser() == null ? null : webSession.getUserContext().getSmSessionId(); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java new file mode 100644 index 0000000000..e23aed90e0 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -0,0 +1,88 @@ +package io.cloudbeaver.service.auth; + +import io.cloudbeaver.auth.provider.rp.RPAuthProvider; +import io.cloudbeaver.model.app.WebAppConfiguration; +import io.cloudbeaver.model.app.WebApplication; +import io.cloudbeaver.model.app.WebAuthApplication; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.service.DBWServiceServerConfigurator; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; + +import java.util.HashMap; +import java.util.Map; + +public class ReverseProxyConfigurator implements DBWServiceServerConfigurator { + private static final Log log = Log.getLog(ReverseProxyConfigurator.class); + + @Override + public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { + + } + + @Override + public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { + if (migrationNotNeeded(application)) { + return; + } + migrateConfiguration(application); + } + + @Override + public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { + + } + + private void migrateConfiguration( + @NotNull WebApplication application + ) { + if (!(application instanceof WebAuthApplication authApplication)) { + return; + } + + SMAuthProviderCustomConfiguration smReverseProxyhProviderConfiguration = + authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); + boolean createNew = false; + if (smReverseProxyhProviderConfiguration == null) { + createNew = true; + smReverseProxyhProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyhProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyhProviderConfiguration.setDisplayName("Reverse Proxy"); + smReverseProxyhProviderConfiguration.setDescription( + "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" + ); + smReverseProxyhProviderConfiguration.setIconURL(""); + Map parameters = new HashMap<>(); + parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); + parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); + parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); + smReverseProxyhProviderConfiguration.setParameters(parameters); + } + + if (createNew) { + authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyhProviderConfiguration); + try { + authApplication.flushConfiguration(); + } catch (Exception e) { + log.error("Failed to save server configuration", e); + } + } + } + + private boolean migrationNotNeeded(@NotNull WebApplication application) { + if (!(application instanceof WebAuthApplication authApplication)) { + return true; + } + + if (!authApplication.getAuthConfiguration().isAuthProviderEnabled(RPAuthProvider.AUTH_PROVIDER)) { + log.debug("Reverse proxy provider disabled, migration not needed"); + return true; + } + return false; + } +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java deleted file mode 100644 index 567be05983..0000000000 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverserProxyConfigurator.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.cloudbeaver.service.auth; - -import io.cloudbeaver.model.app.WebAppConfiguration; -import io.cloudbeaver.model.app.WebApplication; -import io.cloudbeaver.model.session.WebSession; -import io.cloudbeaver.service.DBWServiceServerConfigurator; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.DBException; - -public class ReverserProxyConfigurator implements DBWServiceServerConfigurator { - @Override - public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { - - } - - @Override - public void migrateConfigurationIfNeeded(@NotNull WebApplication application) throws DBException { - DBWServiceServerConfigurator.super.migrateConfigurationIfNeeded(application); // - } - - @Override - public void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException { - - } -} diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index d992e5a5f4..35ec349ec6 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -28,12 +28,12 @@ - + - - - - + + + + diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 8d8d0a0244..c021ca676f 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -48,6 +48,7 @@ public class RPAuthProvider implements SMAuthProviderExternal { public static final String X_ROLE_TE = "X-Role-TE"; public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; + public static final String X_LOGOUT_URL = "X-logout-url"; public static final String AUTH_PROVIDER = "reverseProxy"; @NotNull From 7649de1b587e1a5393078dda450cff6631f9a5e1 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 15 Jan 2024 16:29:52 +0400 Subject: [PATCH 22/33] CB-4389. Added check for already exist configuration --- .../service/auth/ReverseProxyConfigurator.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index e23aed90e0..f57f37de4c 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -19,8 +19,11 @@ public class ReverseProxyConfigurator implements DBWServiceServerConfigurator { private static final Log log = Log.getLog(ReverseProxyConfigurator.class); @Override - public void configureServer(@NotNull WebApplication application, @Nullable WebSession session, @NotNull WebAppConfiguration appConfig) throws DBException { - + public void configureServer( + @NotNull WebApplication application, + @Nullable WebSession session, + @NotNull WebAppConfiguration appConfig + ) throws DBException { } @Override @@ -83,6 +86,11 @@ private boolean migrationNotNeeded(@NotNull WebApplication application) { log.debug("Reverse proxy provider disabled, migration not needed"); return true; } + + if (authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER) != null) { + log.debug("Reverse proxy provider already exist, migration not needed"); + return true; + } return false; } } From e7cc7567ff7cd126636a17ffe3fddff4a23f634c Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 16 Jan 2024 15:09:46 +0400 Subject: [PATCH 23/33] CB-4389. Refactor after review --- .../auth/ReverseProxyConfigurator.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index f57f37de4c..23b7f40182 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -46,29 +46,24 @@ private void migrateConfiguration( return; } - SMAuthProviderCustomConfiguration smReverseProxyhProviderConfiguration = + SMAuthProviderCustomConfiguration smReverseProxyProviderConfiguration = authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); - boolean createNew = false; - if (smReverseProxyhProviderConfiguration == null) { - createNew = true; - smReverseProxyhProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyhProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyhProviderConfiguration.setDisplayName("Reverse Proxy"); - smReverseProxyhProviderConfiguration.setDescription( + if (smReverseProxyProviderConfiguration == null) { + smReverseProxyProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration .setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration .setDisplayName("Reverse Proxy"); + smReverseProxyProviderConfiguration .setDescription( "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" ); - smReverseProxyhProviderConfiguration.setIconURL(""); + smReverseProxyProviderConfiguration .setIconURL(""); Map parameters = new HashMap<>(); parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); - smReverseProxyhProviderConfiguration.setParameters(parameters); - } - - if (createNew) { - authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyhProviderConfiguration); + smReverseProxyProviderConfiguration .setParameters(parameters); + authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyProviderConfiguration ); try { authApplication.flushConfiguration(); } catch (Exception e) { @@ -87,7 +82,11 @@ private boolean migrationNotNeeded(@NotNull WebApplication application) { return true; } - if (authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER) != null) { + boolean isReverseProxyConfigured = authApplication.getAuthConfiguration() + .getAuthCustomConfigurations().stream() + .anyMatch(p -> p.getProvider().equals(RPAuthProvider.AUTH_PROVIDER)); + + if (isReverseProxyConfigured) { log.debug("Reverse proxy provider already exist, migration not needed"); return true; } From 10a93cb53185f20d4c20f325c000a9e19a39f969 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 16 Jan 2024 19:57:14 +0400 Subject: [PATCH 24/33] CB-4389. Refactor after review --- .../src/io/cloudbeaver/service/auth/RPConstants.java | 2 +- .../io/cloudbeaver/service/auth/RPSessionHandler.java | 5 ++++- .../service/auth/ReverseProxyConfigurator.java | 11 +++++------ .../io.cloudbeaver.service.security/plugin.xml | 2 +- .../cloudbeaver/auth/provider/rp/RPAuthProvider.java | 1 - 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index c2e3c4b876..ab638eb311 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,7 +1,7 @@ package io.cloudbeaver.service.auth; interface RPConstants { - String PARAM_LOGOUT_URL = "logout-url-header"; + String PARAM_LOGOUT_URL = "logout-url"; String PARAM_USER = "user-header"; String PARAM_TEAM = "team-header"; String PARAM_FIRST_NAME = "first-name-header"; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index c6f05c8949..f72ecfc1a2 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -94,7 +94,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not String role = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_ROLE_NAME), RPAuthProvider.X_ROLE_TE)); String firstName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_FIRST_NAME), RPAuthProvider.X_FIRST_NAME)); String lastName = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LAST_NAME), RPAuthProvider.X_LAST_NAME)); - String logoutUrl = request.getHeader(resolveParam(paramConfigMap.get(RPConstants.PARAM_LOGOUT_URL), RPAuthProvider.X_LOGOUT_URL)); + String logoutUrl = Objects.requireNonNull(configuration).getParameter(RPConstants.PARAM_LOGOUT_URL); List userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|")); if (userName != null) { try { @@ -106,6 +106,9 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not if (!CommonUtils.isEmpty(lastName)) { credentials.put(SMStandardMeta.META_LAST_NAME, lastName); } + if (!CommonUtils.isNotEmpty(logoutUrl)) { + credentials.put("logoutUrl", logoutUrl); + } Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index 23b7f40182..ad914fc43e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -50,19 +50,18 @@ private void migrateConfiguration( authApplication.getAuthConfiguration().getAuthProviderConfiguration(RPAuthProvider.AUTH_PROVIDER); if (smReverseProxyProviderConfiguration == null) { smReverseProxyProviderConfiguration = new SMAuthProviderCustomConfiguration(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyProviderConfiguration .setProvider(RPAuthProvider.AUTH_PROVIDER); - smReverseProxyProviderConfiguration .setDisplayName("Reverse Proxy"); - smReverseProxyProviderConfiguration .setDescription( + smReverseProxyProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); + smReverseProxyProviderConfiguration.setDisplayName("Reverse Proxy"); + smReverseProxyProviderConfiguration.setDescription( "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" ); smReverseProxyProviderConfiguration .setIconURL(""); Map parameters = new HashMap<>(); parameters.put(RPConstants.PARAM_USER, RPAuthProvider.X_USER); - parameters.put(RPConstants.PARAM_LOGOUT_URL, RPAuthProvider.X_LOGOUT_URL); - parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_LOGOUT_URL); + parameters.put(RPConstants.PARAM_TEAM, RPAuthProvider.X_TEAM); parameters.put(RPConstants.PARAM_FIRST_NAME, RPAuthProvider.X_FIRST_NAME); parameters.put(RPConstants.PARAM_LAST_NAME, RPAuthProvider.X_LAST_NAME); - smReverseProxyProviderConfiguration .setParameters(parameters); + smReverseProxyProviderConfiguration.setParameters(parameters); authApplication.getAuthConfiguration().addAuthProviderConfiguration(smReverseProxyProviderConfiguration ); try { authApplication.flushConfiguration(); diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index 35ec349ec6..5878dca3c7 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -28,7 +28,7 @@ - + diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index c021ca676f..8d8d0a0244 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -48,7 +48,6 @@ public class RPAuthProvider implements SMAuthProviderExternal { public static final String X_ROLE_TE = "X-Role-TE"; public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; - public static final String X_LOGOUT_URL = "X-logout-url"; public static final String AUTH_PROVIDER = "reverseProxy"; @NotNull From 285e1a757ac1cf702e202009cca5011e2a79529a Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Wed, 17 Jan 2024 14:34:00 +0400 Subject: [PATCH 25/33] CB-4389. Refactor after review --- .../auth/SMSignOutLinkProvider.java | 27 +++++++++++++++++++ .../cloudbeaver/service/auth/RPConstants.java | 16 +++++++++++ .../service/auth/RPSessionHandler.java | 2 +- .../auth/ReverseProxyConfigurator.java | 16 +++++++++++ .../auth/provider/rp/RPAuthProvider.java | 13 ++++++++- 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java new file mode 100644 index 0000000000..583e1bc1d7 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java @@ -0,0 +1,27 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ +package io.cloudbeaver.auth; + +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; + +import java.util.Map; + +public interface SMSignOutLinkProvider { + @NotNull + String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java index ab638eb311..304be1493a 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPConstants.java @@ -1,3 +1,19 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ package io.cloudbeaver.service.auth; interface RPConstants { diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index f72ecfc1a2..bdff95ef77 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -123,7 +123,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not webSession.getSessionId(), currentSmSessionId, sessionParameters, - WebSession.CB_SESSION_TYPE, authProvider.getId(), null, userCredentials); + WebSession.CB_SESSION_TYPE, authProvider.getId(), configuration.getId(), userCredentials); new WebSessionAuthProcessor(webSession, smAuthInfo, false).authenticateSession(); log.debug(MessageFormat.format( "Successful reverse proxy authentication: user ''{0}'' with teams {1}", userName, userTeams)); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index ad914fc43e..c0e337b4b4 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -1,3 +1,19 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * 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. + */ package io.cloudbeaver.service.auth; import io.cloudbeaver.auth.provider.rp.RPAuthProvider; diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 8d8d0a0244..813a9aa928 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -18,14 +18,18 @@ import io.cloudbeaver.DBWUserIdentity; import io.cloudbeaver.auth.SMAuthProviderExternal; +import io.cloudbeaver.auth.SMAuthProviderFederated; +import io.cloudbeaver.auth.SMSignOutLinkProvider; import io.cloudbeaver.auth.provider.local.LocalAuthSession; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebUser; +import io.cloudbeaver.utils.WebAppUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPObject; +import org.jkiss.dbeaver.model.auth.SMAuthProvider; import org.jkiss.dbeaver.model.auth.SMSession; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; @@ -37,7 +41,7 @@ import java.util.HashMap; import java.util.Map; -public class RPAuthProvider implements SMAuthProviderExternal { +public class RPAuthProvider implements SMAuthProviderExternal, SMSignOutLinkProvider { private static final Log log = Log.getLog(RPAuthProvider.class); @@ -122,4 +126,11 @@ public void closeSession(@NotNull SMSession mainSession, SMSession session) thro public void refreshSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, SMSession session) throws DBException { } + + @NotNull + @Override + public String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException { + return providerConfig.get("logout-url").toString(); + } + } From 2e1826c3667bf9a1f42ea881b5fee2af2a6955c1 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 16 Jan 2024 12:36:19 +0100 Subject: [PATCH 26/33] CB-4039 adds logout with context for Okta Identity Provider --- .../core-utils/src/getUniqueId.test.ts | 30 +++++++++++++++++++ webapp/packages/core-utils/src/getUniqueId.ts | 10 +++++++ webapp/packages/core-utils/src/index.ts | 1 + 3 files changed, 41 insertions(+) create mode 100644 webapp/packages/core-utils/src/getUniqueId.test.ts create mode 100644 webapp/packages/core-utils/src/getUniqueId.ts diff --git a/webapp/packages/core-utils/src/getUniqueId.test.ts b/webapp/packages/core-utils/src/getUniqueId.test.ts new file mode 100644 index 0000000000..6265873278 --- /dev/null +++ b/webapp/packages/core-utils/src/getUniqueId.test.ts @@ -0,0 +1,30 @@ +import { getUniqueId } from './getUniqueId'; + +describe('getUniqueId', () => { + const uniqueIdsCount = 100; + + it('should return unique ids', () => { + const ids = new Set(); + for (let i = 0; i < uniqueIdsCount; i++) { + ids.add(getUniqueId()); + } + expect(ids.size).toBe(uniqueIdsCount); + }); + + it('should return unique ids in parallel', async () => { + const ids = new Set(); + const promises = []; + for (let i = 0; i < uniqueIdsCount; i++) { + promises.push( + new Promise(resolve => { + setTimeout(() => { + ids.add(getUniqueId()); + resolve(true); + }, Math.random() * 100); + }), + ); + } + await Promise.all(promises); + expect(ids.size).toBe(uniqueIdsCount); + }); +}); diff --git a/webapp/packages/core-utils/src/getUniqueId.ts b/webapp/packages/core-utils/src/getUniqueId.ts new file mode 100644 index 0000000000..f142d2297a --- /dev/null +++ b/webapp/packages/core-utils/src/getUniqueId.ts @@ -0,0 +1,10 @@ +function getNextIdGenerator() { + let id = 0; + return () => id++; +} + +const getNextId = getNextIdGenerator(); + +export function getUniqueId() { + return getNextId(); +} diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index 449c232bc6..81fe04bcd2 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -35,6 +35,7 @@ export * from './isPropertiesEqual'; export * from './isSafari'; export * from './isSameDay'; export * from './isValuesEqual'; +export * from './getUniqueId'; export * from './md5'; export * from './MetadataMap'; export * from './OrderedMap'; From 41ef8c55360159f7bf181984fb27d43690194d14 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 17 Jan 2024 11:41:21 +0100 Subject: [PATCH 27/33] CB-4039 okta logout redirect moved to AuthenticationService --- .../core-utils/src/getUniqueId.test.ts | 30 ------------------- webapp/packages/core-utils/src/getUniqueId.ts | 10 ------- webapp/packages/core-utils/src/index.ts | 1 - 3 files changed, 41 deletions(-) delete mode 100644 webapp/packages/core-utils/src/getUniqueId.test.ts delete mode 100644 webapp/packages/core-utils/src/getUniqueId.ts diff --git a/webapp/packages/core-utils/src/getUniqueId.test.ts b/webapp/packages/core-utils/src/getUniqueId.test.ts deleted file mode 100644 index 6265873278..0000000000 --- a/webapp/packages/core-utils/src/getUniqueId.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getUniqueId } from './getUniqueId'; - -describe('getUniqueId', () => { - const uniqueIdsCount = 100; - - it('should return unique ids', () => { - const ids = new Set(); - for (let i = 0; i < uniqueIdsCount; i++) { - ids.add(getUniqueId()); - } - expect(ids.size).toBe(uniqueIdsCount); - }); - - it('should return unique ids in parallel', async () => { - const ids = new Set(); - const promises = []; - for (let i = 0; i < uniqueIdsCount; i++) { - promises.push( - new Promise(resolve => { - setTimeout(() => { - ids.add(getUniqueId()); - resolve(true); - }, Math.random() * 100); - }), - ); - } - await Promise.all(promises); - expect(ids.size).toBe(uniqueIdsCount); - }); -}); diff --git a/webapp/packages/core-utils/src/getUniqueId.ts b/webapp/packages/core-utils/src/getUniqueId.ts deleted file mode 100644 index f142d2297a..0000000000 --- a/webapp/packages/core-utils/src/getUniqueId.ts +++ /dev/null @@ -1,10 +0,0 @@ -function getNextIdGenerator() { - let id = 0; - return () => id++; -} - -const getNextId = getNextIdGenerator(); - -export function getUniqueId() { - return getNextId(); -} diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index 81fe04bcd2..449c232bc6 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -35,7 +35,6 @@ export * from './isPropertiesEqual'; export * from './isSafari'; export * from './isSameDay'; export * from './isValuesEqual'; -export * from './getUniqueId'; export * from './md5'; export * from './MetadataMap'; export * from './OrderedMap'; From f2569fcfd1f16fae37ed4870935a2c4ef3113be0 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Wed, 17 Jan 2024 21:23:50 +0400 Subject: [PATCH 28/33] CB-4389. Merge logout logic into branch --- .../io/cloudbeaver/auth/SMAuthProviderFederated.java | 7 +------ .../io/cloudbeaver/auth/SMSignOutLinkProvider.java | 12 +++++++++++- .../cloudbeaver/auth/provider/rp/RPAuthProvider.java | 10 ++++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java index 1ec4d407ed..31975908e3 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMAuthProviderFederated.java @@ -32,12 +32,7 @@ public interface SMAuthProviderFederated { @NotNull String getSignInLink(String id, @NotNull Map providerConfig) throws DBException; - /** - * @return a common link for logout, not related with the user context - */ - @NotNull - String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException; - + @Override default String getUserSignOutLink( @NotNull SMAuthProviderCustomConfiguration providerConfig, @NotNull Map userCredentials diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java index 583e1bc1d7..3cbd7f70b1 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/SMSignOutLinkProvider.java @@ -18,10 +18,20 @@ import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import java.util.Map; public interface SMSignOutLinkProvider { + + /** + * @return a common link for logout, not related with the user context + */ @NotNull - String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException; + + String getUserSignOutLink( + @NotNull SMAuthProviderCustomConfiguration providerConfig, + @NotNull Map userCredentials + ) throws DBException; } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 813a9aa928..0a9c736f27 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -53,6 +53,7 @@ public class RPAuthProvider implements SMAuthProviderExternal, SMSign public static final String X_FIRST_NAME = "X-First-name"; public static final String X_LAST_NAME = "X-Last-name"; public static final String AUTH_PROVIDER = "reverseProxy"; + public static final String LOGOUT_URL = "logout-url"; @NotNull @Override @@ -129,8 +130,13 @@ public void refreshSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSessi @NotNull @Override - public String getSignOutLink(String id, @NotNull Map providerConfig) throws DBException { - return providerConfig.get("logout-url").toString(); + public String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException { + return providerConfig.get(LOGOUT_URL).toString(); + } + + @Override + public String getUserSignOutLink(@NotNull SMAuthProviderCustomConfiguration providerConfig, @NotNull Map userCredentials) throws DBException { + return providerConfig.getParameters().get(LOGOUT_URL).toString(); } } From 8b4dd63f3ab81ca9c559dd06f1f1ba414c9e5a44 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Mon, 22 Jan 2024 14:11:22 +0400 Subject: [PATCH 29/33] CB-4389. Delete param after review --- .../src/io/cloudbeaver/service/auth/RPSessionHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index bdff95ef77..858908ee4b 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -112,7 +112,6 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not Map sessionParameters = webSession.getSessionParameters(); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams); sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLE, role); - sessionParameters.put(SMConstants.SESSION_PARAM_LOGOUT_URL, logoutUrl); Map userCredentials = authProviderExternal.authExternalUser( webSession.getProgressMonitor(), null, credentials); String currentSmSessionId = webSession.getUser() == null ? null : webSession.getUserContext().getSmSessionId(); From c68e688d90b24edc6edfa24e5b99621ef50a6b0e Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Mon, 22 Jan 2024 16:38:19 +0100 Subject: [PATCH 30/33] CB-4546 shows hex/base64 only on blob column selection (#2314) Co-authored-by: s.teleshev Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> --- .../TextValue/isTextValuePresentationAvailable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/isTextValuePresentationAvailable.ts b/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/isTextValuePresentationAvailable.ts index be80d9edba..00fa9e9bc8 100644 --- a/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/isTextValuePresentationAvailable.ts +++ b/webapp/packages/plugin-data-viewer/src/ValuePanelPresentation/TextValue/isTextValuePresentationAvailable.ts @@ -31,5 +31,5 @@ export function isBlobPresentationAvailable(context: IDataValuePanelProps Date: Tue, 23 Jan 2024 16:18:43 +0400 Subject: [PATCH 31/33] CB-4389. Fix logout redirect and npe for logout parameter --- .../service/auth/RPSessionHandler.java | 2 +- .../service/auth/impl/WebServiceAuthImpl.java | 19 ++++++++++++++----- .../auth/provider/rp/RPAuthProvider.java | 9 ++++----- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java index 021e509678..4088b0beaa 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/RPSessionHandler.java @@ -106,7 +106,7 @@ public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @Not if (!CommonUtils.isEmpty(lastName)) { credentials.put(SMStandardMeta.META_LAST_NAME, lastName); } - if (!CommonUtils.isNotEmpty(logoutUrl)) { + if (CommonUtils.isNotEmpty(logoutUrl)) { credentials.put("logoutUrl", logoutUrl); } Map sessionParameters = webSession.getSessionParameters(); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index 0d1f3db208..a8367e940e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -19,6 +19,7 @@ import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebServiceUtils; import io.cloudbeaver.auth.SMAuthProviderFederated; +import io.cloudbeaver.auth.SMSignOutLinkProvider; import io.cloudbeaver.auth.provider.local.LocalAuthProvider; import io.cloudbeaver.model.WebPropertyInfo; import io.cloudbeaver.model.session.WebAuthInfo; @@ -148,8 +149,8 @@ public WebLogoutInfo authLogout( var cbApp = CBApplication.getInstance(); for (WebAuthInfo removedInfo : removedInfos) { if (removedInfo.getAuthProviderDescriptor() - .getInstance() instanceof SMAuthProviderFederated federatedProvider - && removedInfo.getAuthSession() instanceof SMSessionExternal externalSession + .getInstance() instanceof SMSignOutLinkProvider federatedProvider + && removedInfo.getAuthSession() != null ) { var providerConfig = cbApp.getAuthConfiguration().getAuthProviderConfiguration(removedInfo.getAuthConfiguration()); @@ -157,9 +158,17 @@ public WebLogoutInfo authLogout( log.warn(removedInfo.getAuthConfiguration() + " provider configuration wasn't found"); continue; } - String logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, - externalSession.getAuthParameters()); - logoutUrls.add(logoutUrl); + String logoutUrl; + if (removedInfo.getAuthSession() instanceof SMSessionExternal externalSession) { + logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, + externalSession.getAuthParameters()); + } else { + logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, + Map.of()); + } + if (CommonUtils.isNotEmpty(logoutUrl)) { + logoutUrls.add(logoutUrl); + } } } return new WebLogoutInfo(logoutUrls); diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java index 0a9c736f27..14106ec3cb 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/rp/RPAuthProvider.java @@ -18,18 +18,15 @@ import io.cloudbeaver.DBWUserIdentity; import io.cloudbeaver.auth.SMAuthProviderExternal; -import io.cloudbeaver.auth.SMAuthProviderFederated; import io.cloudbeaver.auth.SMSignOutLinkProvider; import io.cloudbeaver.auth.provider.local.LocalAuthSession; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebUser; -import io.cloudbeaver.utils.WebAppUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPObject; -import org.jkiss.dbeaver.model.auth.SMAuthProvider; import org.jkiss.dbeaver.model.auth.SMSession; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; @@ -131,12 +128,14 @@ public void refreshSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSessi @NotNull @Override public String getCommonSignOutLink(String id, @NotNull Map providerConfig) throws DBException { - return providerConfig.get(LOGOUT_URL).toString(); + return providerConfig.get(LOGOUT_URL) != null ? providerConfig.get(LOGOUT_URL).toString() : ""; } @Override public String getUserSignOutLink(@NotNull SMAuthProviderCustomConfiguration providerConfig, @NotNull Map userCredentials) throws DBException { - return providerConfig.getParameters().get(LOGOUT_URL).toString(); + return providerConfig.getParameters().get(LOGOUT_URL) != null ? + providerConfig.getParameters().get(LOGOUT_URL).toString() : + null; } } From b43f63997028b7aeefd5c58e6132f40dc5a7dfe4 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 23 Jan 2024 16:43:54 +0400 Subject: [PATCH 32/33] CB-4389. Fix logout redirect and npe for logout parameter --- .../cloudbeaver/service/auth/impl/WebServiceAuthImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index a8367e940e..f590be3c38 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -149,7 +149,7 @@ public WebLogoutInfo authLogout( var cbApp = CBApplication.getInstance(); for (WebAuthInfo removedInfo : removedInfos) { if (removedInfo.getAuthProviderDescriptor() - .getInstance() instanceof SMSignOutLinkProvider federatedProvider + .getInstance() instanceof SMSignOutLinkProvider provider && removedInfo.getAuthSession() != null ) { var providerConfig = @@ -160,10 +160,10 @@ public WebLogoutInfo authLogout( } String logoutUrl; if (removedInfo.getAuthSession() instanceof SMSessionExternal externalSession) { - logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, + logoutUrl = provider.getUserSignOutLink(providerConfig, externalSession.getAuthParameters()); } else { - logoutUrl = federatedProvider.getUserSignOutLink(providerConfig, + logoutUrl = provider.getUserSignOutLink(providerConfig, Map.of()); } if (CommonUtils.isNotEmpty(logoutUrl)) { From 4c3f48160baaac79ea50288e7f087e329dfed4e6 Mon Sep 17 00:00:00 2001 From: Denis Sinelnikov Date: Tue, 23 Jan 2024 18:06:43 +0400 Subject: [PATCH 33/33] CB-4389. Change version --- .../io/cloudbeaver/service/auth/ReverseProxyConfigurator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java index c0e337b4b4..ee415c0ffc 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java @@ -69,7 +69,7 @@ private void migrateConfiguration( smReverseProxyProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER); smReverseProxyProviderConfiguration.setDisplayName("Reverse Proxy"); smReverseProxyProviderConfiguration.setDescription( - "Automatically created provider after changing Reverse Proxy configuration way in 23.3.3 version" + "Automatically created provider after changing Reverse Proxy configuration way in 23.3.4 version" ); smReverseProxyProviderConfiguration .setIconURL(""); Map parameters = new HashMap<>();