GUACAMOLE-1266: Merge implement SAML Single Logout

This commit is contained in:
Virtually Nick authored and GitHub committed 2026-02-01 07:59:18 -05:00
commit 693f05f4e3
5 files changed
+170 -1

No files matched your search

@@ -36,6 +36,52 @@
<relativePath>../../</relativePath>
</parent>
<build>
<plugins>
<!-- JS/CSS Minification Plugin -->
<plugin>
<groupId>com.github.buckelieg</groupId>
<artifactId>minify-maven-plugin</artifactId>
<executions>
<execution>
<id>default-cli</id>
<configuration>
<charset>UTF-8</charset>
<webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
<webappTargetDir>${project.build.directory}/classes</webappTargetDir>
<jsSourceDir>/</jsSourceDir>
<jsTargetDir>/</jsTargetDir>
<jsFinalFile>saml.js</jsFinalFile>
<jsSourceFiles>
<jsSourceFile>license.txt</jsSourceFile>
</jsSourceFiles>
<jsSourceIncludes>
<jsSourceInclude>**/*.js</jsSourceInclude>
</jsSourceIncludes>
<jsEngine>CLOSURE</jsEngine>
<!-- Disable warnings for JSDoc annotations -->
<closureWarningLevels>
<misplacedTypeAnnotation>OFF</misplacedTypeAnnotation>
<nonStandardJsDocs>OFF</nonStandardJsDocs>
</closureWarningLevels>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Guacamole Extension API -->
@@ -24,9 +24,11 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.net.URI;
import java.util.Arrays;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.auth.saml.user.SAMLAuthenticatedUser;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.saml.acs.AssertedIdentity;
import org.apache.guacamole.auth.saml.conf.ConfigurationService;
import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager;
import org.apache.guacamole.auth.saml.acs.SAMLService;
import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
@@ -68,6 +70,12 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
@Inject
private SAMLService saml;
/**
* Service for retrieving SAML configuration information.
*/
@Inject
private ConfigurationService confService;
/**
* Return the value of the session identifier associated with the given
* credentials, or null if no session identifier is found in the
@@ -117,9 +125,25 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
return saml.createRequest();
}
@Override
public URI getLogoutURI(String idToken) throws GuacamoleException {
// If no logout endpoint is configured, return null
URI logoutEndpoint = confService.getLogoutEndpoint();
if (logoutEndpoint == null)
return null;
// Build the logout URI with post-logout redirect
UriBuilder logoutUriBuilder = UriBuilder.fromUri(logoutEndpoint);
logoutUriBuilder.queryParam("RelayState",
confService.getPostLogoutRedirectURI());
return logoutUriBuilder.build();
}
@Override
public void shutdown() {
sessionManager.shutdown();
}
}
@@ -190,6 +190,30 @@ public class ConfigurationService {
};
/**
* The logout endpoint (URI) of the SAML IdP. When configured, users will
* be redirected to this endpoint when they log out of Guacamole.
*/
private static final URIGuacamoleProperty SAML_LOGOUT_ENDPOINT =
new URIGuacamoleProperty() {
@Override
public String getName() { return "saml-logout-endpoint"; }
};
/**
* The URI to redirect to after logout is complete. Defaults to the
* callback URL if not specified.
*/
private static final URIGuacamoleProperty SAML_POST_LOGOUT_REDIRECT_URI =
new URIGuacamoleProperty() {
@Override
public String getName() { return "saml-post-logout-redirect-uri"; }
};
/**
* The Guacamole server environment.
*/
@@ -390,6 +414,37 @@ public class ConfigurationService {
return environment.getProperty(SAML_PRIVATE_KEY_PATH);
}
/**
* Returns the logout endpoint (URI) of the SAML IdP. If not configured,
* null will be returned and logout will only end the Guacamole session.
*
* @return
* The logout endpoint of the SAML IdP, or null if not configured.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public URI getLogoutEndpoint() throws GuacamoleException {
return environment.getProperty(SAML_LOGOUT_ENDPOINT);
}
/**
* Returns the URI to redirect to after logout is complete. If not
* configured, the callback URL will be used.
*
* @return
* The post-logout redirect URI.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public URI getPostLogoutRedirectURI() throws GuacamoleException {
URI postLogoutUri = environment.getProperty(SAML_POST_LOGOUT_REDIRECT_URI);
if (postLogoutUri != null)
return postLogoutUri;
return getCallbackUrl();
}
/**
* Returns the contents of a small file, such as a private key or certificate into
* a String. If the file does not exist, or cannot be read for any reason, an exception
@@ -17,6 +17,10 @@
"styles/sso-providers.css"
],
"js" : [
"saml.min.js"
],
"html" : [
"html/sso-providers.html",
"html/sso-provider-saml.html"
@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* The module for code implementing SSO using SAML.
*/
angular.module('guacSsoSaml', []);
/**
* Redirect to the SSO logout endpoint when the user logs out, allowing
* Single Logout to occur if configured.
*/
angular.module('guacSsoSaml').run(['$rootScope', '$window',
function guacSsoSamlLogout($rootScope, $window) {
// Redirect to SSO logout endpoint when user logs out
$rootScope.$on('guacLogout', function handleLogout() {
$window.location.href = 'api/ext/saml/logout';
});
}]);
// Ensure the guacSsoSaml module is loaded along with the rest of the app
angular.module('index').requires.push('guacSsoSaml');