mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-1364: Refactor all SSO extensions beneath common base.
This commit is contained in:
3
extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/.gitignore
vendored
Normal file
3
extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*~
|
||||
target/
|
||||
src/main/resources/generated/
|
@@ -0,0 +1 @@
|
||||
src/main/resources/html/*.html
|
@@ -0,0 +1,98 @@
|
||||
<?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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-auth-sso-saml</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.3.0</version>
|
||||
<name>guacamole-auth-sso-saml</name>
|
||||
<url>http://guacamole.apache.org/</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-auth-sso</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Guacamole Extension API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-ext</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Core SSO support -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-auth-sso-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Guice -->
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Java servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JAX-RS Annotations -->
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>jsr311-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OneLogin SAML Library -->
|
||||
<dependency>
|
||||
<groupId>com.onelogin</groupId>
|
||||
<artifactId>java-saml</artifactId>
|
||||
<version>2.6.0</version>
|
||||
<exclusions>
|
||||
|
||||
<!-- Force consistent version of commons-codec (see below) -->
|
||||
<exclusion>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Force single, specific version of commons-codec (multiple
|
||||
dependencies reference multiple versions of this). -->
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.15</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -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