mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-30 00:23:21 +00:00 
			
		
		
		
	GUACAMOLE-1364: Refactor all SSO extensions beneath common base.
This commit is contained in:
		| @@ -0,0 +1,53 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|     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. | ||||
| --> | ||||
| <assembly | ||||
|     xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" | ||||
|     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|     xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> | ||||
|      | ||||
|     <id>dist</id> | ||||
|     <baseDirectory>${project.artifactId}-${project.version}</baseDirectory> | ||||
|  | ||||
|     <!-- Output tar.gz --> | ||||
|     <formats> | ||||
|         <format>tar.gz</format> | ||||
|     </formats> | ||||
|  | ||||
|     <!-- Include licenses and extension .jar --> | ||||
|     <fileSets> | ||||
|  | ||||
|         <!-- Include licenses --> | ||||
|         <fileSet> | ||||
|             <outputDirectory></outputDirectory> | ||||
|             <directory>target/licenses</directory> | ||||
|         </fileSet> | ||||
|  | ||||
|         <!-- Include extension .jar --> | ||||
|         <fileSet> | ||||
|             <directory>target</directory> | ||||
|             <outputDirectory></outputDirectory> | ||||
|             <includes> | ||||
|                 <include>*.jar</include> | ||||
|             </includes> | ||||
|         </fileSet> | ||||
|  | ||||
|     </fileSets> | ||||
|  | ||||
| </assembly> | ||||
| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
| import java.net.URI; | ||||
| import java.util.Arrays; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| 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.acs.AuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLService; | ||||
| import org.apache.guacamole.form.Field; | ||||
| import org.apache.guacamole.form.RedirectField; | ||||
| import org.apache.guacamole.language.TranslatableMessage; | ||||
| import org.apache.guacamole.net.auth.AuthenticatedUser; | ||||
| import org.apache.guacamole.net.auth.Credentials; | ||||
| import org.apache.guacamole.net.auth.credentials.CredentialsInfo; | ||||
| import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; | ||||
|  | ||||
| /** | ||||
|  * Service that authenticates Guacamole users by processing the responses of | ||||
|  * SAML identity providers. | ||||
|  */ | ||||
| public class AuthenticationProviderService { | ||||
|  | ||||
|     /** | ||||
|      * The name of the query parameter that identifies an active authentication | ||||
|      * session (in-progress SAML authentication attempt). | ||||
|      */ | ||||
|     public static final String AUTH_SESSION_QUERY_PARAM = "state"; | ||||
|  | ||||
|     /** | ||||
|      * Provider for AuthenticatedUser objects. | ||||
|      */ | ||||
|     @Inject | ||||
|     private Provider<SAMLAuthenticatedUser> authenticatedUserProvider; | ||||
|  | ||||
|     /** | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Service for processing SAML requests/responses. | ||||
|      */ | ||||
|     @Inject | ||||
|     private SAMLService saml; | ||||
|  | ||||
|     /** | ||||
|      * Returns an AuthenticatedUser representing the user authenticated by the | ||||
|      * given credentials. | ||||
|      * | ||||
|      * @param credentials | ||||
|      *     The credentials to use for authentication. | ||||
|      * | ||||
|      * @return | ||||
|      *     An AuthenticatedUser representing the user authenticated by the | ||||
|      *     given credentials. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If an error occurs while authenticating the user, or if access is | ||||
|      *     denied. | ||||
|      */ | ||||
|     public AuthenticatedUser authenticateUser(Credentials credentials) | ||||
|             throws GuacamoleException { | ||||
|  | ||||
|         // No authentication can be attempted without a corresponding HTTP | ||||
|         // request | ||||
|         HttpServletRequest request = credentials.getRequest(); | ||||
|         if (request == null) | ||||
|             return null; | ||||
|  | ||||
|         // Use established SAML identity if already provided by the SAML IdP | ||||
|         AssertedIdentity identity = sessionManager.getIdentity(request.getParameter(AUTH_SESSION_QUERY_PARAM)); | ||||
|         if (identity != null) { | ||||
|  | ||||
|             // Back-port the username to the credentials | ||||
|             credentials.setUsername(identity.getUsername()); | ||||
|  | ||||
|             // Configure the AuthenticatedUser and return it | ||||
|             SAMLAuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); | ||||
|             authenticatedUser.init(identity, credentials); | ||||
|             return authenticatedUser; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Redirect to SAML IdP if no SAML identity is associated with the | ||||
|         // Guacamole authentication request | ||||
|         URI authUri = saml.createRequest(); | ||||
|         throw new GuacamoleInsufficientCredentialsException("Redirecting to SAML IdP.", | ||||
|                 new CredentialsInfo(Arrays.asList(new Field[] { | ||||
|                     new RedirectField("samlRedirect", authUri, new TranslatableMessage("LOGIN.INFO_SAML_REDIRECT_PENDING")) | ||||
|                 })) | ||||
|         ); | ||||
|  | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml; | ||||
|  | ||||
| import com.google.inject.Guice; | ||||
| import com.google.inject.Injector; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource; | ||||
| import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.user.SAMLAuthenticatedUser; | ||||
| import org.apache.guacamole.net.auth.AuthenticatedUser; | ||||
| import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; | ||||
| import org.apache.guacamole.net.auth.Credentials; | ||||
| import org.apache.guacamole.net.auth.TokenInjectingUserContext; | ||||
| import org.apache.guacamole.net.auth.UserContext; | ||||
|  | ||||
| /** | ||||
|  * AuthenticationProvider implementation that authenticates Guacamole users | ||||
|  * against a SAML SSO Identity Provider (IdP). This module does not provide any | ||||
|  * storage for connection information, and must be layered with other modules | ||||
|  * for authenticated users to have access to Guacamole connections. | ||||
|  */ | ||||
| public class SAMLAuthenticationProvider extends AbstractAuthenticationProvider { | ||||
|  | ||||
|     /** | ||||
|      * Injector which will manage the object graph of this authentication | ||||
|      * provider. | ||||
|      */ | ||||
|     private final Injector injector; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new SAMLAuthenticationProvider that authenticates users | ||||
|      * against a SAML IdP. | ||||
|      */ | ||||
|     public SAMLAuthenticationProvider() { | ||||
|  | ||||
|         // Set up Guice injector. | ||||
|         injector = Guice.createInjector( | ||||
|             new SAMLAuthenticationProviderModule(this) | ||||
|         ); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getIdentifier() { | ||||
|         return "saml"; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Object getResource() throws GuacamoleException { | ||||
|         return injector.getInstance(AssertionConsumerServiceResource.class); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public AuthenticatedUser authenticateUser(Credentials credentials) | ||||
|             throws GuacamoleException { | ||||
|  | ||||
|         // Attempt to authenticate user with given credentials | ||||
|         AuthenticationProviderService authProviderService = | ||||
|                 injector.getInstance(AuthenticationProviderService.class); | ||||
|         return authProviderService.authenticateUser(credentials); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public UserContext decorate(UserContext context, | ||||
|             AuthenticatedUser authenticatedUser, Credentials credentials) | ||||
|             throws GuacamoleException { | ||||
|  | ||||
|         // Only decorate if the user authenticated with SAML | ||||
|         if (!(authenticatedUser instanceof SAMLAuthenticatedUser)) | ||||
|             return context; | ||||
|  | ||||
|         // Apply SAML-specific tokens to all connections / connection groups | ||||
|         return new TokenInjectingUserContext(context, | ||||
|                 ((SAMLAuthenticatedUser) authenticatedUser).getTokens()); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void shutdown() { | ||||
|         injector.getInstance(AuthenticationSessionManager.class).shutdown(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml; | ||||
|  | ||||
| import com.google.inject.AbstractModule; | ||||
| import org.apache.guacamole.auth.saml.conf.ConfigurationService; | ||||
| import org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource; | ||||
| import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.IdentifierGenerator; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLService; | ||||
| import org.apache.guacamole.environment.Environment; | ||||
| import org.apache.guacamole.environment.LocalEnvironment; | ||||
| import org.apache.guacamole.net.auth.AuthenticationProvider; | ||||
|  | ||||
| /** | ||||
|  * Guice module which configures SAML-specific injections. | ||||
|  */ | ||||
| public class SAMLAuthenticationProviderModule extends AbstractModule { | ||||
|  | ||||
|     /** | ||||
|      * Guacamole server environment. | ||||
|      */ | ||||
|     private final Environment environment; | ||||
|  | ||||
|     /** | ||||
|      * A reference to the SAMLAuthenticationProvider on behalf of which this | ||||
|      * module has configured injection. | ||||
|      */ | ||||
|     private final AuthenticationProvider authProvider; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new SAML authentication provider module which configures | ||||
|      * injection for the SAMLAuthenticationProvider. | ||||
|      * | ||||
|      * @param authProvider | ||||
|      *     The AuthenticationProvider for which injection is being configured. | ||||
|      */ | ||||
|     public SAMLAuthenticationProviderModule(AuthenticationProvider authProvider) { | ||||
|  | ||||
|         // Get local environment | ||||
|         this.environment = LocalEnvironment.getInstance(); | ||||
|  | ||||
|         // Store associated auth provider | ||||
|         this.authProvider = authProvider; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void configure() { | ||||
|  | ||||
|         // Bind core implementations of guacamole-ext classes | ||||
|         bind(AuthenticationProvider.class).toInstance(authProvider); | ||||
|         bind(Environment.class).toInstance(environment); | ||||
|  | ||||
|         // Bind SAML-specific services | ||||
|         bind(AssertionConsumerServiceResource.class); | ||||
|         bind(AuthenticationSessionManager.class); | ||||
|         bind(ConfigurationService.class); | ||||
|         bind(IdentifierGenerator.class); | ||||
|         bind(SAMLService.class); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,134 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| import com.onelogin.saml2.authn.SamlResponse; | ||||
| import com.onelogin.saml2.exception.ValidationError; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import javax.xml.xpath.XPathExpressionException; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.GuacamoleSecurityException; | ||||
|  | ||||
| /** | ||||
|  * Representation of a user's identity as asserted by a SAML IdP. | ||||
|  */ | ||||
| public class AssertedIdentity { | ||||
|  | ||||
|     /** | ||||
|      * The original SAML response received from the SAML IdP that asserted | ||||
|      * the user's identity. | ||||
|      */ | ||||
|     private final SamlResponse response; | ||||
|  | ||||
|     /** | ||||
|      * The user's Guacamole username. | ||||
|      */ | ||||
|     private final String username; | ||||
|  | ||||
|     /** | ||||
|      * All attributes included in the original SAML response. Attributes may | ||||
|      * possibly be associated with multiple values. | ||||
|      */ | ||||
|     private final Map<String, List<String>> attributes; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new AssertedIdentity representing the identity asserted by the | ||||
|      * SAML IdP in the given response. | ||||
|      * | ||||
|      * @param response | ||||
|      *     The response received from the SAML IdP. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If the given SAML response cannot be parsed or is missing required | ||||
|      *     information. | ||||
|      */ | ||||
|     public AssertedIdentity(SamlResponse response) throws GuacamoleException { | ||||
|  | ||||
|         // Parse user identity from SAML response | ||||
|         String nameId; | ||||
|         try { | ||||
|             nameId = response.getNameId(); | ||||
|             if (nameId == null) | ||||
|                 throw new GuacamoleSecurityException("SAML response did not " | ||||
|                         + "include the relevant user's identity (no name ID)."); | ||||
|         } | ||||
|  | ||||
|         // Unfortunately, getNameId() is declared as "throws Exception", so | ||||
|         // this error handling has to be pretty generic | ||||
|         catch (Exception e) { | ||||
|             throw new GuacamoleSecurityException("User identity (name ID) " | ||||
|                     + "could not be retrieved from the SAML response: " + e.getMessage(), e); | ||||
|         } | ||||
|  | ||||
|         // Retrieve any provided attributes | ||||
|         Map<String, List<String>> responseAttributes; | ||||
|         try { | ||||
|             responseAttributes = Collections.unmodifiableMap(response.getAttributes()); | ||||
|         } | ||||
|         catch (XPathExpressionException | ValidationError e) { | ||||
|             throw new GuacamoleSecurityException("SAML attributes could not " | ||||
|                     + "be parsed from the SAML response: " + e.getMessage(), e); | ||||
|         } | ||||
|  | ||||
|         this.response = response; | ||||
|         this.username = nameId.toLowerCase(); // Canonicalize username as lowercase | ||||
|         this.attributes = responseAttributes; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the username of the Guacamole user whose identity was asserted | ||||
|      * by the SAML IdP. | ||||
|      * | ||||
|      * @return | ||||
|      *     The username of the Guacamole user whose identity was asserted by | ||||
|      *     the SAML IdP. | ||||
|      */ | ||||
|     public String getUsername() { | ||||
|         return username; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a Map containing all attributes included in the original SAML | ||||
|      * response that asserted this user's identity. Attributes may possibly be | ||||
|      * associated with multiple values. | ||||
|      * | ||||
|      * @return | ||||
|      *     A Map of all attributes included in the original SAML response. | ||||
|      */ | ||||
|     public Map<String, List<String>> getAttributes() { | ||||
|         return attributes; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether the identity asserted by the original SAML response is | ||||
|      * still valid. An asserted identity may cease to be valid after creation | ||||
|      * if it has expired according to the timestamps included in the response. | ||||
|      * | ||||
|      * @return | ||||
|      *     true if the original SAML response is still valid, false otherwise. | ||||
|      */ | ||||
|     public boolean isValid() { | ||||
|         return response.isValid(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,134 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import java.net.URI; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.ws.rs.core.Response; | ||||
| import javax.ws.rs.FormParam; | ||||
| import javax.ws.rs.Path; | ||||
| import javax.ws.rs.POST; | ||||
| import javax.ws.rs.core.Context; | ||||
| import javax.ws.rs.core.UriBuilder; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.auth.saml.AuthenticationProviderService; | ||||
| import org.apache.guacamole.auth.saml.conf.ConfigurationService; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| /** | ||||
|  * REST API resource that provides a SAML assertion consumer service (ACS) | ||||
|  * endpoint. SAML identity providers will issue an HTTP POST to this endpoint | ||||
|  * asserting the user's identity when the user has successfully authenticated. | ||||
|  */ | ||||
| public class AssertionConsumerServiceResource { | ||||
|  | ||||
|     /** | ||||
|      * Logger for this class. | ||||
|      */ | ||||
|     private static final Logger logger = LoggerFactory.getLogger(AssertionConsumerServiceResource.class); | ||||
|  | ||||
|     /** | ||||
|      * The configuration service for this module. | ||||
|      */ | ||||
|     @Inject | ||||
|     private ConfigurationService confService; | ||||
|  | ||||
|     /** | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Service for processing SAML requests/responses. | ||||
|      */ | ||||
|     @Inject | ||||
|     private SAMLService saml; | ||||
|  | ||||
|     /** | ||||
|      * Processes the SAML response submitted by the SAML IdP via an HTTP POST. | ||||
|      * If SSO has been successful, the user is redirected back to Guacamole to | ||||
|      * complete authentication. If SSO has failed, the user is redirected back | ||||
|      * to Guacamole to re-attempt authentication. | ||||
|      * | ||||
|      * @param relayState | ||||
|      *     The "RelayState" value originally provided in the SAML request, | ||||
|      *     which in our case is the transient the session identifier of the | ||||
|      *     in-progress authentication attempt. The SAML standard requires that | ||||
|      *     the identity provider include the "RelayState" value it received | ||||
|      *     alongside its SAML response. | ||||
|      * | ||||
|      * @param samlResponse | ||||
|      *     The encoded response returned by the SAML IdP. | ||||
|      * | ||||
|      * @param consumedRequest | ||||
|      *     The HttpServletRequest associated with the SAML response. The | ||||
|      *     parameters of this request may not be accessible, as the request may | ||||
|      *     have been fully consumed by JAX-RS. | ||||
|      * | ||||
|      * @return | ||||
|      *     An HTTP Response that will redirect the user back to Guacamole, | ||||
|      *     with an internal state identifier included in the URL such that the | ||||
|      *     the Guacamole side of authentication can complete. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If configuration information required for processing SAML responses | ||||
|      *     cannot be read. | ||||
|      */ | ||||
|     @POST | ||||
|     @Path("callback") | ||||
|     public Response processSamlResponse( | ||||
|             @FormParam("RelayState") String relayState, | ||||
|             @FormParam("SAMLResponse") String samlResponse, | ||||
|             @Context HttpServletRequest consumedRequest) | ||||
|             throws GuacamoleException { | ||||
|  | ||||
|         URI guacBase = confService.getCallbackUrl(); | ||||
|  | ||||
|         try { | ||||
|  | ||||
|             // Validate and parse identity asserted by SAML IdP | ||||
|             AuthenticationSession session = saml.processResponse( | ||||
|                     consumedRequest.getRequestURL().toString(), | ||||
|                     relayState, samlResponse); | ||||
|  | ||||
|             // Store asserted identity for future retrieval via redirect | ||||
|             String identifier = sessionManager.defer(session); | ||||
|  | ||||
|             // Redirect user such that Guacamole's authentication system can | ||||
|             // retrieve the relevant SAML identity (stored above) | ||||
|             return Response.seeOther(UriBuilder.fromUri(guacBase) | ||||
|                     .queryParam(AuthenticationProviderService.AUTH_SESSION_QUERY_PARAM, identifier) | ||||
|                     .build() | ||||
|             ).build(); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // If invalid, redirect back to main page to re-attempt authentication | ||||
|         catch (GuacamoleException e) { | ||||
|             logger.warn("Authentication attempted with an invalid SAML response: {}", e.getMessage()); | ||||
|             logger.debug("Received SAML response failed validation.", e); | ||||
|             return Response.seeOther(guacBase).build(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,113 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| /** | ||||
|  * Representation of an in-progress SAML authentication attempt. | ||||
|  */ | ||||
| public class AuthenticationSession { | ||||
|  | ||||
|     /** | ||||
|      * The absolute point in time after which this authentication session is | ||||
|      * invalid. This value is a UNIX epoch timestamp, as may be returned by | ||||
|      * {@link System#currentTimeMillis()}. | ||||
|      */ | ||||
|     private final long expirationTimestamp; | ||||
|  | ||||
|     /** | ||||
|      * The request ID of the SAML request associated with the authentication | ||||
|      * attempt. | ||||
|      */ | ||||
|     private final String requestId; | ||||
|  | ||||
|     /** | ||||
|      * The identity asserted by the SAML IdP, or null if authentication has not | ||||
|      * yet completed successfully. | ||||
|      */ | ||||
|     private AssertedIdentity identity = null; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new AuthenticationSession representing an in-progress SAML | ||||
|      * authentication attempt. | ||||
|      * | ||||
|      * @param requestId | ||||
|      *     The request ID of the SAML request associated with the | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @param expires | ||||
|      *     The number of milliseconds that may elapse before this session must | ||||
|      *     be considered invalid. | ||||
|      */ | ||||
|     public AuthenticationSession(String requestId, long expires) { | ||||
|         this.expirationTimestamp = System.currentTimeMillis() + expires; | ||||
|         this.requestId = requestId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether this authentication session is still valid (has not yet | ||||
|      * expired). If an identity has been asserted by the SAML IdP, this | ||||
|      * considers also whether the SAML response asserting that identity has | ||||
|      * expired. | ||||
|      * | ||||
|      * @return | ||||
|      *     true if this authentication session is still valid, false if it has | ||||
|      *     expired. | ||||
|      */ | ||||
|     public boolean isValid() { | ||||
|         return System.currentTimeMillis() < expirationTimestamp | ||||
|                 && (identity == null || identity.isValid()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the request ID of the SAML request associated with the | ||||
|      * authentication attempt. | ||||
|      * | ||||
|      * @return | ||||
|      *     The request ID of the SAML request associated with the | ||||
|      *     authentication attempt. | ||||
|      */ | ||||
|     public String getRequestID() { | ||||
|         return requestId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Marks this authentication attempt as completed and successful, with the | ||||
|      * user having been asserted as having the given identity by the SAML IdP. | ||||
|      * | ||||
|      * @param identity | ||||
|      *     The identity asserted by the SAML IdP. | ||||
|      */ | ||||
|     public void setIdentity(AssertedIdentity identity) { | ||||
|         this.identity = identity; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the identity asserted by the SAML IdP. If authentication has not | ||||
|      * yet completed successfully, this will be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The identity asserted by the SAML IdP, or null if authentication has | ||||
|      *     not yet completed successfully. | ||||
|      */ | ||||
|     public AssertedIdentity getIdentity() { | ||||
|         return identity; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,152 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| import com.google.common.base.Predicates; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.concurrent.ConcurrentMap; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| /** | ||||
|  * Manager service that temporarily stores SAML authentication attempts while | ||||
|  * the authentication flow is underway. Authentication attempts are represented | ||||
|  * as temporary authentication sessions, allowing authentication attempts to | ||||
|  * span multiple requests and redirects. Invalid or stale authentication | ||||
|  * sessions are automatically purged from storage. | ||||
|  */ | ||||
| @Singleton | ||||
| public class AuthenticationSessionManager { | ||||
|  | ||||
|     /** | ||||
|      * Generator of arbitrary, unique, unpredictable identifiers. | ||||
|      */ | ||||
|     @Inject | ||||
|     private IdentifierGenerator idGenerator; | ||||
|  | ||||
|     /** | ||||
|      * Map of authentication session identifiers to their associated | ||||
|      * {@link AuthenticationSession}. | ||||
|      */ | ||||
|     private final ConcurrentMap<String, AuthenticationSession> sessions = | ||||
|         new ConcurrentHashMap<>(); | ||||
|  | ||||
|     /** | ||||
|      * Executor service which runs the periodic cleanup task | ||||
|      */ | ||||
|     private final ScheduledExecutorService executor = | ||||
|             Executors.newScheduledThreadPool(1); | ||||
|  | ||||
|     /** | ||||
|      * Creates a new AuthenticationSessionManager that manages in-progress | ||||
|      * SAML authentication attempts. Invalid, stale sessions are automatically | ||||
|      * cleaned up. | ||||
|      */ | ||||
|     public AuthenticationSessionManager() { | ||||
|         executor.scheduleAtFixedRate(() -> { | ||||
|             sessions.values().removeIf(Predicates.not(AuthenticationSession::isValid)); | ||||
|         }, 1, 1, TimeUnit.MINUTES); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resumes the Guacamole side of the authentication process that was | ||||
|      * previously deferred through a call to defer(). Once invoked, the | ||||
|      * provided value ceases to be valid for future calls to resume(). | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The unique string returned by the call to defer(). For convenience, | ||||
|      *     this value may safely be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The {@link AuthenticationSession} originally provided when defer() | ||||
|      *     was invoked, or null if the session is no longer valid or no such | ||||
|      *     value was returned by defer(). | ||||
|      */ | ||||
|     public AuthenticationSession resume(String identifier) { | ||||
|  | ||||
|         if (identifier != null) { | ||||
|             AuthenticationSession session = sessions.remove(identifier); | ||||
|             if (session != null && session.isValid()) | ||||
|                 return session; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the identity finally asserted by the SAML IdP at the end of the | ||||
|      * authentication process represented by the authentication session with | ||||
|      * the given identifier. If there is no such authentication session, or no | ||||
|      * valid identity has been asserted by the SAML IdP for that session, null | ||||
|      * is returned. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The unique string returned by the call to defer(). For convenience, | ||||
|      *     this value may safely be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The identity finally asserted by the SAML IdP at the end of the | ||||
|      *     authentication process represented by the authentication session | ||||
|      *     with the given identifier, or null if there is no such identity. | ||||
|      */ | ||||
|     public AssertedIdentity getIdentity(String identifier) { | ||||
|  | ||||
|         AuthenticationSession session = resume(identifier); | ||||
|         if (session != null) | ||||
|             return session.getIdentity(); | ||||
|  | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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. | ||||
|      * | ||||
|      * @param session | ||||
|      *     The {@link AuthenticationSession} representing the in-progress SAML | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable string that may be used to represent the | ||||
|      *     given session when calling resume(). | ||||
|      */ | ||||
|     public String defer(AuthenticationSession session) { | ||||
|         String identifier = idGenerator.generateIdentifier(); | ||||
|         sessions.put(identifier, session); | ||||
|         return identifier; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shuts down the executor service that periodically removes all invalid | ||||
|      * authentication sessions. This must be invoked when the SAML extension is | ||||
|      * shut down in order to avoid resource leaks. | ||||
|      */ | ||||
|     public void shutdown() { | ||||
|         executor.shutdownNow(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| import com.google.common.io.BaseEncoding; | ||||
| import com.google.inject.Singleton; | ||||
| import java.security.SecureRandom; | ||||
|  | ||||
| /** | ||||
|  * Generator of unique and unpredictable identifiers. Each generated identifier | ||||
|  * is an arbitrary, random string produced using a cryptographically-secure | ||||
|  * random number generator and consists of at least 256 bits. | ||||
|  */ | ||||
| @Singleton | ||||
| public class IdentifierGenerator { | ||||
|  | ||||
|     /** | ||||
|      * Cryptographically-secure random number generator for generating unique | ||||
|      * identifiers. | ||||
|      */ | ||||
|     private final SecureRandom secureRandom = new SecureRandom(); | ||||
|  | ||||
|     /** | ||||
|      * Generates a unique and unpredictable identifier. Each identifier is at | ||||
|      * least 256-bit and produced using a cryptographically-secure random | ||||
|      * number generator. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable identifier. | ||||
|      */ | ||||
|     public String generateIdentifier() { | ||||
|         byte[] bytes = new byte[33]; | ||||
|         secureRandom.nextBytes(bytes); | ||||
|         return BaseEncoding.base64().encode(bytes); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,188 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
| import com.onelogin.saml2.authn.AuthnRequest; | ||||
| import com.onelogin.saml2.authn.SamlResponse; | ||||
| import com.onelogin.saml2.exception.SettingsException; | ||||
| import com.onelogin.saml2.exception.ValidationError; | ||||
| 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; | ||||
| import org.apache.guacamole.GuacamoleSecurityException; | ||||
| import org.apache.guacamole.GuacamoleServerException; | ||||
| import org.apache.guacamole.auth.saml.conf.ConfigurationService; | ||||
| import org.xml.sax.SAXException; | ||||
|  | ||||
| /** | ||||
|  * Service which abstracts the internals of handling SAML requests and | ||||
|  * responses. | ||||
|  */ | ||||
| @Singleton | ||||
| public class SAMLService { | ||||
|  | ||||
|     /** | ||||
|      * Service for retrieving SAML configuration information. | ||||
|      */ | ||||
|     @Inject | ||||
|     private ConfigurationService confService; | ||||
|  | ||||
|     /** | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new SAML request, beginning the overall authentication flow | ||||
|      * that will ultimately result in an asserted user identity if the user is | ||||
|      * successfully authenticated by the SAML IdP. The URI of the SSO endpoint | ||||
|      * of the SAML IdP that the user must be redirected to for the | ||||
|      * authentication process to continue is returned. | ||||
|      * | ||||
|      * @return | ||||
|      *     The URI of the SSO endpoint of the SAML IdP that the user must be | ||||
|      *     redirected to. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If an error prevents the SAML request and redirect URI from being | ||||
|      *     generated. | ||||
|      */ | ||||
|     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(); | ||||
|         } | ||||
|         catch (IOException e) { | ||||
|             throw new GuacamoleServerException("SAML authentication request " | ||||
|                     + "could not be encoded: " + e.getMessage()); | ||||
|         } | ||||
|         catch (URISyntaxException e) { | ||||
|             throw new GuacamoleServerException("SAML IdP redirect could not " | ||||
|                     + "be generated due to an error in the URI syntax: " | ||||
|                     + e.getMessage()); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Processes the given SAML response, as received by the SAML ACS endpoint | ||||
|      * at the given URL, producing an {@link AuthenticationSession} that now | ||||
|      * includes a valid assertion of the user's identity. If the SAML response | ||||
|      * is invalid in any way, an exception is thrown. | ||||
|      * | ||||
|      * @param url | ||||
|      *     The URL of the ACS endpoint that received the SAML response. This | ||||
|      *     should be the URL pointing to the single POST-handling endpoint of | ||||
|      *     {@link AssertionConsumerServiceResource}. | ||||
|      * | ||||
|      * @param relayState | ||||
|      *     The "RelayState" value originally provided in the SAML request, | ||||
|      *     which in our case is the transient the session identifier of the | ||||
|      *     in-progress authentication attempt. The SAML standard requires that | ||||
|      *     the identity provider include the "RelayState" value it received | ||||
|      *     alongside its SAML response. | ||||
|      * | ||||
|      * @param encodedResponse | ||||
|      *     The response received from the SAML IdP via the ACS endpoint at the | ||||
|      *     given URL. | ||||
|      * | ||||
|      * @return | ||||
|      *     The {@link AuthenticationSession} associated with the in-progress | ||||
|      *     authentication attempt, now associated with the {@link AssertedIdentity} | ||||
|      *     representing the identity of the user asserted by the SAML IdP. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If the given SAML response is not valid, or if the configuration | ||||
|      *     information required to validate or decrypt the response cannot be | ||||
|      *     read. | ||||
|      */ | ||||
|     public AuthenticationSession processResponse(String url, String relayState, | ||||
|             String encodedResponse) throws GuacamoleException { | ||||
|  | ||||
|         if (relayState == null) | ||||
|             throw new GuacamoleSecurityException("\"RelayState\" value " | ||||
|                     + "is missing from SAML response."); | ||||
|  | ||||
|         AuthenticationSession session = sessionManager.resume(relayState); | ||||
|         if (session == null) | ||||
|             throw new GuacamoleSecurityException("\"RelayState\" value " | ||||
|                     + "included with SAML response is not valid."); | ||||
|  | ||||
|         try { | ||||
|  | ||||
|             // Decode received SAML response | ||||
|             SamlResponse response = new SamlResponse(confService.getSamlSettings(), | ||||
|                     url, encodedResponse); | ||||
|  | ||||
|             // Validate SAML response timestamp, signature, etc. | ||||
|             if (!response.isValid(session.getRequestID())) { | ||||
|                 Exception validationException = response.getValidationException(); | ||||
|                 throw new GuacamoleSecurityException("SAML response did not " | ||||
|                         + "pass validation: " + validationException.getMessage(), | ||||
|                         validationException); | ||||
|             } | ||||
|  | ||||
|             // Parse identity asserted by SAML IdP | ||||
|             session.setIdentity(new AssertedIdentity(response)); | ||||
|             return session; | ||||
|  | ||||
|         } | ||||
|         catch (ValidationError e) { | ||||
|             throw new GuacamoleSecurityException("SAML response did not pass " | ||||
|                     + "validation: " + e.getMessage(), e); | ||||
|         } | ||||
|         catch (SettingsException e) { | ||||
|             throw new GuacamoleServerException("Current SAML settings are " | ||||
|                     + "insufficient to decrypt/parse the received SAML " | ||||
|                     + "response.", e); | ||||
|         } | ||||
|         catch (ParserConfigurationException | SAXException | XPathExpressionException e) { | ||||
|             throw new GuacamoleServerException("XML contents of SAML " | ||||
|                     + "response could not be parsed.", e); | ||||
|         } | ||||
|         catch (IOException e) { | ||||
|             throw new GuacamoleServerException("Contents of SAML response " | ||||
|                     + "could not be decrypted/read.", e); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,394 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.conf; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import com.onelogin.saml2.settings.IdPMetadataParser; | ||||
| import com.onelogin.saml2.settings.Saml2Settings; | ||||
| import com.onelogin.saml2.settings.SettingsBuilder; | ||||
| import com.onelogin.saml2.util.Constants; | ||||
| import java.net.URI; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import javax.ws.rs.core.UriBuilder; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.GuacamoleServerException; | ||||
| import org.apache.guacamole.environment.Environment; | ||||
| import org.apache.guacamole.properties.BooleanGuacamoleProperty; | ||||
| import org.apache.guacamole.properties.IntegerGuacamoleProperty; | ||||
| import org.apache.guacamole.properties.StringGuacamoleProperty; | ||||
| import org.apache.guacamole.properties.URIGuacamoleProperty; | ||||
|  | ||||
| /** | ||||
|  * Service for retrieving configuration information regarding the SAML | ||||
|  * authentication module. | ||||
|  */ | ||||
| public class ConfigurationService { | ||||
|  | ||||
|     /** | ||||
|      * The URI of the file containing the XML Metadata associated with the | ||||
|      * SAML IdP. | ||||
|      */ | ||||
|     private static final URIGuacamoleProperty SAML_IDP_METADATA_URL = | ||||
|             new URIGuacamoleProperty() { | ||||
|  | ||||
|         @Override | ||||
|         public String getName() { return "saml-idp-metadata-url"; } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The URL of the SAML IdP. | ||||
|      */ | ||||
|     private static final URIGuacamoleProperty SAML_IDP_URL = | ||||
|             new URIGuacamoleProperty() { | ||||
|  | ||||
|         @Override | ||||
|         public String getName() { return "saml-idp-url"; } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The URL identifier for this SAML client. | ||||
|      */ | ||||
|     private static final URIGuacamoleProperty SAML_ENTITY_ID = | ||||
|             new URIGuacamoleProperty() { | ||||
|  | ||||
|         @Override | ||||
|         public String getName() { return "saml-entity-id"; } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The callback URL to use for SAML IdP, normally the base | ||||
|      * of the Guacamole install. The SAML extensions callback | ||||
|      * endpoint will be appended to this value. | ||||
|      */ | ||||
|     private static final URIGuacamoleProperty SAML_CALLBACK_URL = | ||||
|             new URIGuacamoleProperty() { | ||||
|  | ||||
|         @Override | ||||
|         public String getName() { return "saml-callback-url"; } | ||||
|  | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Whether or not debugging should be enabled in the SAML library to help | ||||
|      * track down errors. | ||||
|      */ | ||||
|     private static final BooleanGuacamoleProperty SAML_DEBUG = | ||||
|             new BooleanGuacamoleProperty() { | ||||
|      | ||||
|         @Override | ||||
|         public String getName() { return "saml-debug"; } | ||||
|                  | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Whether or not to enable compression for the SAML request. | ||||
|      */ | ||||
|     private static final BooleanGuacamoleProperty SAML_COMPRESS_REQUEST = | ||||
|             new BooleanGuacamoleProperty() { | ||||
|              | ||||
|         @Override | ||||
|         public String getName() { return "saml-compress-request"; } | ||||
|                  | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Whether or not to enable compression for the SAML response. | ||||
|      */ | ||||
|     private static final BooleanGuacamoleProperty SAML_COMPRESS_RESPONSE = | ||||
|             new BooleanGuacamoleProperty() { | ||||
|              | ||||
|         @Override | ||||
|         public String getName() { return "saml-compress-response"; } | ||||
|                  | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * Whether or not to enforce strict SAML security during processing. | ||||
|      */ | ||||
|     private static final BooleanGuacamoleProperty SAML_STRICT = | ||||
|             new BooleanGuacamoleProperty() { | ||||
|              | ||||
|         @Override | ||||
|         public String getName() { return "saml-strict"; } | ||||
|          | ||||
|     }; | ||||
|      | ||||
|     /** | ||||
|      * The property that defines what attribute the SAML provider will return | ||||
|      * that contains group membership for the authenticated user. | ||||
|      */ | ||||
|     private static final StringGuacamoleProperty SAML_GROUP_ATTRIBUTE = | ||||
|             new StringGuacamoleProperty() { | ||||
|              | ||||
|         @Override | ||||
|         public String getName() { return "saml-group-attribute"; } | ||||
|                  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The maximum amount of time to allow for an in-progress SAML | ||||
|      * authentication attempt to be completed, in minutes. A user that takes | ||||
|      * longer than this amount of time to complete authentication with their | ||||
|      * identity provider will be redirected back to the identity provider to | ||||
|      * try again. | ||||
|      */ | ||||
|     private static final IntegerGuacamoleProperty SAML_AUTH_TIMEOUT = | ||||
|             new IntegerGuacamoleProperty() { | ||||
|              | ||||
|         @Override | ||||
|         public String getName() { return "saml-auth-timeout"; } | ||||
|                  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The Guacamole server environment. | ||||
|      */ | ||||
|     @Inject | ||||
|     private Environment environment; | ||||
|  | ||||
|     /** | ||||
|      * Returns the URL to be submitted as the client ID to the SAML IdP, as | ||||
|      * configured in guacamole.properties. | ||||
|      * | ||||
|      * @return | ||||
|      *     The URL to send to the SAML IdP as the Client Identifier. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private URI getEntityId() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_ENTITY_ID); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The URI that contains the metadata that the SAML client should | ||||
|      * use to communicate with the SAML IdP. This can either be a remote | ||||
|      * URL of a server that provides this, or can be a URI to a file on the | ||||
|      * local filesystem. The metadata file is usually generated by the SAML IdP | ||||
|      * and should be uploaded to the system where the Guacamole client is | ||||
|      * running. | ||||
|      * | ||||
|      * @return | ||||
|      *     The URI of the file containing the metadata used by the SAML client | ||||
|      *     when it communicates with the SAML IdP. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If guacamole.properties cannot be parsed, or if the client | ||||
|      *     metadata is missing. | ||||
|      */ | ||||
|     private URI getIdpMetadata() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_IDP_METADATA_URL); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the URL used to log in to the SAML IdP. | ||||
|      * | ||||
|      * @return | ||||
|      *     The URL used to log in to the SAML IdP. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private URI getIdpUrl() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_IDP_URL); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The callback URL used for the SAML IdP to POST a response | ||||
|      * to upon completion of authentication, normally the base | ||||
|      * of the Guacamole install. | ||||
|      * | ||||
|      * @return | ||||
|      *     The callback URL to be sent to the SAML IdP that will | ||||
|      *     be POSTed to upon completion of SAML authentication. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If guacamole.properties cannot be parsed, or the property | ||||
|      *     is missing. | ||||
|      */ | ||||
|     public URI getCallbackUrl() throws GuacamoleException { | ||||
|         return environment.getRequiredProperty(SAML_CALLBACK_URL); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return the Boolean value that indicates whether SAML client debugging | ||||
|      * will be enabled, as configured in guacamole.properties. The default is | ||||
|      * false, and debug information will not be generated or logged. | ||||
|      *  | ||||
|      * @return | ||||
|      *     True if debugging should be enabled in the SAML library, otherwise | ||||
|      *     false. | ||||
|      *  | ||||
|      * @throws GuacamoleException  | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private boolean getDebug() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_DEBUG, false); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return the Boolean value that indicates whether or not compression of | ||||
|      * SAML requests to the IdP should be enabled or not, as configured in | ||||
|      * guacamole.properties. The default is to enable compression. | ||||
|      *  | ||||
|      * @return | ||||
|      *     True if compression should be enabled when sending the SAML request, | ||||
|      *     otherwise false. | ||||
|      *  | ||||
|      * @throws GuacamoleException  | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private boolean getCompressRequest() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_COMPRESS_REQUEST, true); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return a Boolean value that indicates whether or not the SAML login | ||||
|      * should enforce strict security controls, as configured in | ||||
|      * guacamole.properties. By default this is true, and should be set to | ||||
|      * true in any production environment. | ||||
|      *  | ||||
|      * @return | ||||
|      *     True if the SAML login should enforce strict security checks, | ||||
|      *     otherwise false. | ||||
|      *  | ||||
|      * @throws GuacamoleException  | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private boolean getStrict() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_STRICT, true); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return a Boolean value that indicates whether or not compression should | ||||
|      * be requested from the server when the SAML response is returned, as | ||||
|      * configured in guacamole.properties. The default is to request that the | ||||
|      * response be compressed. | ||||
|      *  | ||||
|      * @return | ||||
|      *     True if compression should be requested from the server for the SAML | ||||
|      *     response. | ||||
|      *  | ||||
|      * @throws GuacamoleException  | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     private boolean getCompressResponse() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_COMPRESS_RESPONSE, true); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Return the name of the attribute that will be supplied by the identity | ||||
|      * provider that contains the groups of which this user is a member. | ||||
|      *  | ||||
|      * @return | ||||
|      *     The name of the attribute that contains the user groups. | ||||
|      *  | ||||
|      * @throws GuacamoleException  | ||||
|      *     If guacamole.properties cannot be parsed. | ||||
|      */ | ||||
|     public String getGroupAttribute() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_GROUP_ATTRIBUTE, "groups"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the maximum amount of time to allow for an in-progress SAML | ||||
|      * authentication attempt to be completed, in minutes. A user that takes | ||||
|      * longer than this amount of time to complete authentication with their | ||||
|      * identity provider will be redirected back to the identity provider to | ||||
|      * try again. | ||||
|      * | ||||
|      * @return | ||||
|      *     The maximum amount of time to allow for an in-progress SAML | ||||
|      *     authentication attempt to be completed, in minutes. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If the authentication timeout cannot be parsed. | ||||
|      */ | ||||
|     public int getAuthenticationTimeout() throws GuacamoleException { | ||||
|         return environment.getProperty(SAML_AUTH_TIMEOUT, 5); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the collection of SAML settings used to initialize the client. | ||||
|      * | ||||
|      * @return | ||||
|      *     The collection of SAML settings used to initialize the SAML client. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If guacamole.properties cannot be parsed or if required parameters | ||||
|      *     are missing. | ||||
|      */ | ||||
|     public Saml2Settings getSamlSettings() throws GuacamoleException { | ||||
|  | ||||
|         // Try to get the XML file, first. | ||||
|         URI idpMetadata = getIdpMetadata(); | ||||
|         Map<String, Object> samlMap; | ||||
|         if (idpMetadata != null) { | ||||
|             try { | ||||
|                 samlMap = IdPMetadataParser.parseRemoteXML(idpMetadata.toURL()); | ||||
|             } | ||||
|             catch (Exception e) { | ||||
|                 throw new GuacamoleServerException( | ||||
|                         "Could not parse SAML IdP Metadata file.", e); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // If no XML metadata is provided, fall-back to individual values. | ||||
|         else { | ||||
|             samlMap = new HashMap<>(); | ||||
|             samlMap.put(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY, | ||||
|                     getIdpUrl().toString()); | ||||
|             samlMap.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, | ||||
|                     getIdpUrl().toString()); | ||||
|             samlMap.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY, | ||||
|                     Constants.BINDING_HTTP_REDIRECT); | ||||
|         } | ||||
|  | ||||
|         // Read entity ID from properties if not provided within metadata XML | ||||
|         if (!samlMap.containsKey(SettingsBuilder.SP_ENTITYID_PROPERTY_KEY)) { | ||||
|             URI entityId = getEntityId(); | ||||
|             if (entityId == null) | ||||
|                 throw new GuacamoleServerException("SAML Entity ID was not found" | ||||
|                         + " in either the metadata XML file or guacamole.properties"); | ||||
|             samlMap.put(SettingsBuilder.SP_ENTITYID_PROPERTY_KEY, entityId.toString()); | ||||
|         } | ||||
|  | ||||
|         // Derive ACS URL from properties if not provided within metadata XML | ||||
|         if (!samlMap.containsKey(SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY)) { | ||||
|             samlMap.put(SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, | ||||
|                     UriBuilder.fromUri(getCallbackUrl()).path("api/ext/saml/callback").build().toString()); | ||||
|         } | ||||
|  | ||||
|         SettingsBuilder samlBuilder = new SettingsBuilder(); | ||||
|         Saml2Settings samlSettings = samlBuilder.fromValues(samlMap).build(); | ||||
|         samlSettings.setStrict(getStrict()); | ||||
|         samlSettings.setDebug(getDebug()); | ||||
|         samlSettings.setCompressRequest(getCompressRequest()); | ||||
|         samlSettings.setCompressResponse(getCompressResponse()); | ||||
|      | ||||
|         return samlSettings; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,178 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| package org.apache.guacamole.auth.saml.user; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import java.util.Collections; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.stream.Collectors; | ||||
| 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.net.auth.AbstractAuthenticatedUser; | ||||
| import org.apache.guacamole.net.auth.AuthenticationProvider; | ||||
| import org.apache.guacamole.net.auth.Credentials; | ||||
| import org.apache.guacamole.token.TokenName; | ||||
|  | ||||
| /** | ||||
|  * A SAML-specific implementation of AuthenticatedUser, associating a SAML | ||||
|  * identity and particular set of credentials with the SAML authentication | ||||
|  * provider. | ||||
|  */ | ||||
| public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser { | ||||
|  | ||||
|     /** | ||||
|      * The prefix that should be prepended to all parameter tokens generated | ||||
|      * from SAML attributes. | ||||
|      */ | ||||
|     private static final String SAML_ATTRIBUTE_TOKEN_PREFIX = "SAML_"; | ||||
|  | ||||
|     /** | ||||
|      * Service for retrieving SAML configuration information. | ||||
|      */ | ||||
|     @Inject | ||||
|     private ConfigurationService confService; | ||||
|  | ||||
|     /** | ||||
|      * Reference to the authentication provider associated with this | ||||
|      * authenticated user. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationProvider authProvider; | ||||
|  | ||||
|     /** | ||||
|      * The credentials provided when this user was authenticated. | ||||
|      */ | ||||
|     private Credentials credentials; | ||||
|      | ||||
|     /** | ||||
|      * The effective groups of the authenticated user. | ||||
|      */ | ||||
|     private Set<String> effectiveGroups; | ||||
|      | ||||
|     /** | ||||
|      * Tokens associated with the authenticated user. | ||||
|      */ | ||||
|     private Map<String, String> tokens; | ||||
|  | ||||
|     /** | ||||
|      * Returns a Map of all parameter tokens that should be made available for | ||||
|      * substitution based on the given {@link AssertedIdentity}. The resulting | ||||
|      * Map will contain one parameter token for each SAML attribute in the | ||||
|      * SAML response that originally asserted the user's identity. Attributes | ||||
|      * that have multiple values will be reduced to a single value, taking the | ||||
|      * first available value and discarding the remaining values. | ||||
|      * | ||||
|      * @param identity | ||||
|      *     The {@link AssertedIdentity} representing the user identity | ||||
|      *     asserted by the SAML IdP. | ||||
|      * | ||||
|      * @return | ||||
|      *     A Map of key and single value pairs that should be made available | ||||
|      *     for substitution as parameter tokens. | ||||
|      */ | ||||
|     private Map<String, String> getTokens(AssertedIdentity identity) { | ||||
|         return Collections.unmodifiableMap(identity.getAttributes().entrySet() | ||||
|                 .stream() | ||||
|                 .filter((entry) -> !entry.getValue().isEmpty()) | ||||
|                 .collect(Collectors.toMap( | ||||
|                     (entry) -> TokenName.canonicalize(entry.getKey(), SAML_ATTRIBUTE_TOKEN_PREFIX), | ||||
|                     (entry) -> entry.getValue().get(0) | ||||
|                 ))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a set of all group memberships asserted by the SAML IdP. | ||||
|      *  | ||||
|      * @param identity | ||||
|      *     The {@link AssertedIdentity} representing the user identity | ||||
|      *     asserted by the SAML IdP. | ||||
|      *  | ||||
|      * @return | ||||
|      *     A set of all groups that the SAML IdP asserts this user is a | ||||
|      *     member of. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If the configuration information necessary to retrieve group | ||||
|      *     memberships from a SAML response cannot be read. | ||||
|      */ | ||||
|     private Set<String> getGroups(AssertedIdentity identity) | ||||
|             throws GuacamoleException { | ||||
|  | ||||
|         List<String> samlGroups = identity.getAttributes().get(confService.getGroupAttribute()); | ||||
|         if (samlGroups == null || samlGroups.isEmpty()) | ||||
|             return Collections.emptySet(); | ||||
|  | ||||
|         return Collections.unmodifiableSet(new HashSet<>(samlGroups)); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Initializes this AuthenticatedUser using the given | ||||
|      * {@link AssertedIdentity} and credentials. | ||||
|      * | ||||
|      * @param identity | ||||
|      *     The {@link AssertedIdentity} representing the user identity | ||||
|      *     asserted by the SAML IdP. | ||||
|      * | ||||
|      * @param credentials | ||||
|      *     The credentials provided when this user was authenticated. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If configuration information required for processing the user's | ||||
|      *     identity and group memberships cannot be read. | ||||
|      */ | ||||
|     public void init(AssertedIdentity identity, Credentials credentials) | ||||
|             throws GuacamoleException { | ||||
|         this.credentials = credentials; | ||||
|         this.effectiveGroups = getGroups(identity); | ||||
|         this.tokens = getTokens(identity); | ||||
|         setIdentifier(identity.getUsername()); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Returns a Map of tokens associated with this authenticated user. | ||||
|      *  | ||||
|      * @return  | ||||
|      *     A map of token names and values available from this user account. | ||||
|      */ | ||||
|     public Map<String, String> getTokens() { | ||||
|         return tokens; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public AuthenticationProvider getAuthenticationProvider() { | ||||
|         return authProvider; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Credentials getCredentials() { | ||||
|         return credentials; | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public Set<String> getEffectiveUserGroups() { | ||||
|         return effectiveGroups; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| { | ||||
|  | ||||
|     "guacamoleVersion" : "1.3.0", | ||||
|  | ||||
|     "name"      : "SAML Authentication Extension", | ||||
|     "namespace" : "saml", | ||||
|  | ||||
|     "authProviders" : [ | ||||
|         "org.apache.guacamole.auth.saml.SAMLAuthenticationProvider" | ||||
|     ], | ||||
|  | ||||
|     "translations" : [ | ||||
|         "translations/ca.json", | ||||
|         "translations/en.json", | ||||
|         "translations/ko.json", | ||||
|         "translations/fr.json", | ||||
|         "translations/pt.json" | ||||
|     ] | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|  | ||||
|     "DATA_SOURCE_SAML" : { | ||||
|         "NAME" : "Extensión de autenticación SAML" | ||||
|     }, | ||||
|  | ||||
|     "LOGIN" : { | ||||
|         "FIELD_HEADER_SAML"     : "", | ||||
|         "INFO_SAML_REDIRECT_PENDING" : "Por favor espere, redirigiendo al proveedor de identidad ..." | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|  | ||||
|     "DATA_SOURCE_SAML" : { | ||||
|         "NAME" : "SAML Authentication Extension" | ||||
|     }, | ||||
|  | ||||
|     "LOGIN" : { | ||||
|         "FIELD_HEADER_SAML"     : "", | ||||
|         "INFO_SAML_REDIRECT_PENDING" : "Please wait, redirecting to identity provider..." | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|  | ||||
|     "DATA_SOURCE_SAML" : { | ||||
|         "NAME" : "SAML Authentication Extension" | ||||
|     }, | ||||
|  | ||||
|     "LOGIN" : { | ||||
|         "FIELD_HEADER_SAML"     : "", | ||||
|         "INFO_SAML_REDIRECT_PENDING" : "Veuillez patienter, redirection vers le fournisseur d'identité..." | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|  | ||||
|     "DATA_SOURCE_SAML" : { | ||||
|         "NAME" : "SAML 인증 확장 프로그램" | ||||
|     }, | ||||
|  | ||||
|     "LOGIN" : { | ||||
|         "INFO_SAML_REDIRECT_PENDING": "잠시만 기다려주십시오. ID 제공자로 리디렉션 중..." | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|  | ||||
|     "DATA_SOURCE_SAML" : { | ||||
|         "NAME" : "SAML Authentication Extension" | ||||
|     }, | ||||
|  | ||||
|     "LOGIN" : { | ||||
|         "FIELD_HEADER_SAML"     : "", | ||||
|         "INFO_SAML_REDIRECT_PENDING" : "Por favor aguarde, redirecionando para o provedor de indentidade..." | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user