diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/AuthenticationSessionManager.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/AuthenticationSessionManager.java index 198347cda..2e55d2cfc 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/AuthenticationSessionManager.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/AuthenticationSessionManager.java @@ -126,6 +126,8 @@ public class AuthenticationSessionManager { * call to resume(). If authentication is never resumed, the session will * automatically be cleaned up after it ceases to be valid. * + * This method will automatically generate a new identifier. + * * @param session * The {@link AuthenticationSession} representing the in-progress SAML * authentication attempt. @@ -140,6 +142,27 @@ public class AuthenticationSessionManager { return identifier; } + /** + * Defers the Guacamole side of authentication for the user having the + * given authentication session such that it may be later resumed through a + * call to resume(). If authentication is never resumed, the session will + * automatically be cleaned up after it ceases to be valid. + * + * This method accepts an externally generated ID, which should be a UUID + * or similar unique identifier. + * + * @param session + * The {@link AuthenticationSession} representing the in-progress SAML + * authentication attempt. + * + * @param identifier + * A unique and unpredictable string that may be used to represent the + * given session when calling resume(). + */ + public void defer(AuthenticationSession session, String identifier) { + sessions.put(identifier, session); + } + /** * Shuts down the executor service that periodically removes all invalid * authentication sessions. This must be invoked when the SAML extension is diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/SAMLService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/SAMLService.java index bd945203e..c3357eefb 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/SAMLService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/acs/SAMLService.java @@ -21,7 +21,9 @@ package org.apache.guacamole.auth.saml.acs; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.onelogin.saml2.Auth; import com.onelogin.saml2.authn.AuthnRequest; +import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; @@ -29,7 +31,6 @@ import com.onelogin.saml2.settings.Saml2Settings; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import javax.ws.rs.core.UriBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; import org.apache.guacamole.GuacamoleException; @@ -57,6 +58,12 @@ public class SAMLService { @Inject private AuthenticationSessionManager sessionManager; + /** + * Generator of arbitrary, unique, unpredictable identifiers. + */ + @Inject + private IdentifierGenerator idGenerator; + /** * Creates a new SAML request, beginning the overall authentication flow * that will ultimately result in an asserted user identity if the user is @@ -75,20 +82,31 @@ public class SAMLService { public URI createRequest() throws GuacamoleException { Saml2Settings samlSettings = confService.getSamlSettings(); - AuthnRequest samlReq = new AuthnRequest(samlSettings); - - // Create a new authentication session to represent this attempt while - // it is in progress - AuthenticationSession session = new AuthenticationSession(samlReq.getId(), - confService.getAuthenticationTimeout() * 60000L); // Produce redirect for continuing the authentication process with // the SAML IdP try { - return UriBuilder.fromUri(samlSettings.getIdpSingleSignOnServiceUrl().toURI()) - .queryParam("SAMLRequest", samlReq.getEncodedAuthnRequest()) - .queryParam("RelayState", sessionManager.defer(session)) - .build(); + Auth auth = new Auth(samlSettings, null, null); + + // Generate a unique ID to use for the relay state + String identifier = idGenerator.generateIdentifier(); + + // Create the request URL for the SAML IdP + String requestUrl = auth.login( + identifier, + new AuthnRequestParams(false, false, true), + true); + + // Create a new authentication session to represent this attempt while + // it is in progress, using the request ID that was just issued + AuthenticationSession session = new AuthenticationSession( + auth.getLastRequestId(), + confService.getAuthenticationTimeout() * 60000L); + + // Save the session with the unique relay state ID + sessionManager.defer(session, identifier); + + return new URI(requestUrl); } catch (IOException e) { throw new GuacamoleServerException("SAML authentication request " @@ -99,6 +117,11 @@ public class SAMLService { + "be generated due to an error in the URI syntax: " + e.getMessage()); } + catch (SettingsException e) { + throw new GuacamoleServerException("Error while attempting to sign " + + "request using provided private key / certificate: " + + e.getMessage(), e); + } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java index 95862bd34..d2a73c46a 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/conf/ConfigurationService.java @@ -492,7 +492,15 @@ public class ConfigurationService { samlSettings.setDebug(getDebug()); samlSettings.setCompressRequest(getCompressRequest()); samlSettings.setCompressResponse(getCompressResponse()); - + + // Request that the SAML library sign everything that it can, if + // both private key and certificate are specified + if (privateKeyFile != null && certificateFile != null) { + samlSettings.setAuthnRequestsSigned(true); + samlSettings.setLogoutRequestSigned(true); + samlSettings.setLogoutResponseSigned(true); + } + return samlSettings; }