Merge 1.4.0 changes back to master.

This commit is contained in:
James Muehlner
2021-12-03 21:23:39 -08:00
105 changed files with 1140 additions and 1286 deletions

View File

@@ -1,53 +0,0 @@
<?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>

View File

@@ -1,90 +0,0 @@
/*
* 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.cas;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.TokenInjectingUserContext;
import org.apache.guacamole.net.auth.UserContext;
/**
* Guacamole authentication backend which authenticates users using an
* arbitrary external system implementing CAS. No storage for connections is
* provided - only authentication. Storage must be provided by some other
* extension.
*/
public class CASAuthenticationProvider extends AbstractAuthenticationProvider {
/**
* Injector which will manage the object graph of this authentication
* provider.
*/
private final Injector injector;
/**
* Creates a new CASAuthenticationProvider that authenticates users
* against an CAS service
*
* @throws GuacamoleException
* If a required property is missing, or an error occurs while parsing
* a property.
*/
public CASAuthenticationProvider() throws GuacamoleException {
// Set up Guice injector.
injector = Guice.createInjector(
new CASAuthenticationProviderModule(this)
);
}
@Override
public String getIdentifier() {
return "cas";
}
@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 {
if (!(authenticatedUser instanceof CASAuthenticatedUser))
return context;
return new TokenInjectingUserContext(context,
((CASAuthenticatedUser) authenticatedUser).getTokens());
}
}

View File

@@ -1,81 +0,0 @@
/*
* 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.cas;
import org.apache.guacamole.auth.cas.conf.ConfigurationService;
import com.google.inject.AbstractModule;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
/**
* Guice module which configures CAS-specific injections.
*/
public class CASAuthenticationProviderModule extends AbstractModule {
/**
* Guacamole server environment.
*/
private final Environment environment;
/**
* A reference to the CASAuthenticationProvider on behalf of which this
* module has configured injection.
*/
private final AuthenticationProvider authProvider;
/**
* Creates a new CAS authentication provider module which configures
* injection for the CASAuthenticationProvider.
*
* @param authProvider
* The AuthenticationProvider for which injection is being configured.
*
* @throws GuacamoleException
* If an error occurs while retrieving the Guacamole server
* environment.
*/
public CASAuthenticationProviderModule(AuthenticationProvider authProvider)
throws GuacamoleException {
// 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 CAS-specific services
bind(ConfigurationService.class);
bind(TicketValidationService.class);
}
}

View File

@@ -1,78 +0,0 @@
/*
* 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.cas.form;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableMessage;
/**
* Field definition which represents the ticket returned by an CAS service.
* This is processed transparently - the user is redirected to CAS, authenticates
* and then is returned to Guacamole where the ticket field is
* processed.
*/
public class CASTicketField extends RedirectField {
/**
* The parameter that will be present upon successful CAS authentication.
*/
public static final String PARAMETER_NAME = "ticket";
/**
* The standard URI name for the CAS login resource.
*/
private static final String CAS_LOGIN_URI = "login";
/**
* Creates a new CAS "ticket" field which links to the given CAS
* service using the provided client ID. Successful authentication at the
* CAS service will result in the client being redirected to the specified
* redirect URI. The CAS ticket will be embedded in the fragment (the part
* following the hash symbol) of that URI, which the JavaScript side of
* this extension will move to the query parameters.
*
* @param authorizationEndpoint
* The full URL of the endpoint accepting CAS authentication
* requests.
*
* @param redirectURI
* The URI that the CAS service should redirect to upon successful
* authentication.
*
* @param redirectMessage
* The message that will be displayed for the user while the redirect
* is processed. This will be processed through Guacamole's translation
* system.
*/
public CASTicketField(URI authorizationEndpoint, URI redirectURI,
TranslatableMessage redirectMessage) {
super(PARAMETER_NAME, UriBuilder.fromUri(authorizationEndpoint)
.path(CAS_LOGIN_URI)
.queryParam("service", redirectURI)
.build(),
redirectMessage);
}
}

View File

@@ -1,11 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "Backend d'inici de sessió unificat (SSO) CAS"
},
"LOGIN" : {
"INFO_CAS_REDIRECT_PENDING" : "Espereu, redireccionant a l'autenticació CAS ..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_TICKET" : "",
"INFO_CAS_REDIRECT_PENDING" : "Bitte warten, Sie werden zur CAS-Authentifizierung weitergeleitet..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_TICKET" : "",
"INFO_CAS_REDIRECT_PENDING" : "Please wait, redirecting to CAS authentication..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_TICKET" : "",
"INFO_CAS_REDIRECT_PENDING" : "Veuillez patienter, redirection vers l'authentification CAS..."
}
}

View File

@@ -1,7 +0,0 @@
{
"LOGIN" : {
"INFO_CAS_REDIRECT_PENDING" : "CAS認証にリダイレクトしています。"
}
}

View File

@@ -1,7 +0,0 @@
{
"LOGIN" : {
"INFO_CAS_REDIRECT_PENDING" : "기다려주십시오. CAS 인증으로 리디렉션 중..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_TICKET" : "",
"INFO_CAS_REDIRECT_PENDING" : "Por favor aguarde, redirecionando para autenticação CAS..."
}
}

View File

@@ -1,11 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "Бэкенд CAS SSO"
},
"LOGIN" : {
"INFO_CAS_REDIRECT_PENDING" : "Пожалуйста, подождите. Переадресую на страницу аутентификации CAS..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO后端"
},
"LOGIN" : {
"FIELD_HEADER_TICKET" : "",
"INFO_CAS_REDIRECT_PENDING" : "请稍候正在重定向到CAS验证..."
}
}

View File

@@ -1,53 +0,0 @@
<?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>

View File

@@ -1,83 +0,0 @@
/*
* 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.openid;
import com.google.inject.AbstractModule;
import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.token.NonceService;
import org.apache.guacamole.auth.openid.token.TokenValidationService;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AuthenticationProvider;
/**
* Guice module which configures openid-specific injections.
*/
public class OpenIDAuthenticationProviderModule extends AbstractModule {
/**
* Guacamole server environment.
*/
private final Environment environment;
/**
* A reference to the OpenIDAuthenticationProvider on behalf of which this
* module has configured injection.
*/
private final AuthenticationProvider authProvider;
/**
* Creates a new OpenID authentication provider module which configures
* injection for the OpenIDAuthenticationProvider.
*
* @param authProvider
* The AuthenticationProvider for which injection is being configured.
*
* @throws GuacamoleException
* If an error occurs while retrieving the Guacamole server
* environment.
*/
public OpenIDAuthenticationProviderModule(AuthenticationProvider authProvider)
throws GuacamoleException {
// 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 openid-specific services
bind(ConfigurationService.class);
bind(NonceService.class);
bind(TokenValidationService.class);
}
}

View File

@@ -1,87 +0,0 @@
/*
* 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.openid.form;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableMessage;
/**
* Field definition which represents the token returned by an OpenID Connect
* service.
*/
public class TokenField extends RedirectField {
/**
* The standard HTTP parameter which will be included within the URL by all
* OpenID services upon successful authentication and redirect.
*/
public static final String PARAMETER_NAME = "id_token";
/**
* Creates a new field which requests authentication via OpenID connect.
* Successful authentication at the OpenID Connect service will result in
* the client being redirected to the specified redirect URI. The OpenID
* token will be embedded in the fragment (the part following the hash
* symbol) of that URI, which the JavaScript side of this extension will
* move to the query parameters.
*
* @param authorizationEndpoint
* The full URL of the endpoint accepting OpenID authentication
* requests.
*
* @param scope
* The space-delimited list of OpenID scopes to request from the
* identity provider, such as "openid" or "openid email profile".
*
* @param clientID
* The ID of the OpenID client. This is normally determined ahead of
* time by the OpenID service through some manual credential request
* procedure.
*
* @param redirectURI
* The URI that the OpenID service should redirect to upon successful
* authentication.
*
* @param nonce
* A random string unique to this request. To defend against replay
* attacks, this value must cease being valid after its first use.
*
* @param redirectMessage
* The message that will be displayed to the user during redirect. This
* will be processed through Guacamole's translation system.
*/
public TokenField(URI authorizationEndpoint, String scope,
String clientID, URI redirectURI, String nonce,
TranslatableMessage redirectMessage) {
super(PARAMETER_NAME, UriBuilder.fromUri(authorizationEndpoint)
.queryParam("scope", scope)
.queryParam("response_type", "id_token")
.queryParam("client_id", clientID)
.queryParam("redirect_uri", redirectURI)
.queryParam("nonce", nonce)
.build(),
redirectMessage);
}
}

View File

@@ -1,85 +0,0 @@
/*
* 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.openid.user;
import com.google.inject.Inject;
import java.util.Set;
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
/**
* An openid-specific implementation of AuthenticatedUser, associating a
* username, a particular set of credentials and the groups with the
* OpenID authentication provider.
*/
public class AuthenticatedUser extends AbstractAuthenticatedUser {
/**
* 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 groups of the user that was authenticated.
*/
private Set<String> effectiveGroups;
/**
* Initializes this AuthenticatedUser using the given username and
* credentials.
*
* @param username
* The username of the user that was authenticated.
*
* @param credentials
* The credentials provided when this user was authenticated.
*
* @param effectiveGroups
* The groups of the user that was authenticated.
*/
public void init(String username, Credentials credentials, Set<String> effectiveGroups) {
this.credentials = credentials;
this.effectiveGroups = effectiveGroups;
setIdentifier(username);
}
@Override
public AuthenticationProvider getAuthenticationProvider() {
return authProvider;
}
@Override
public Credentials getCredentials() {
return credentials;
}
@Override
public Set<String> getEffectiveUserGroups() {
return effectiveGroups;
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"INFO_OID_REDIRECT_PENDING" : "Espereu, redirigint al proveïdor d'identitat ..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"INFO_OID_REDIRECT_PENDING" : "Please wait, redirecting to identity provider..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"INFO_OID_REDIRECT_PENDING" : "Veuillez patienter, redirection vers le fournisseur d'identité..."
}
}

View File

@@ -1,7 +0,0 @@
{
"LOGIN" : {
"INFO_OID_REDIRECT_PENDING" : "IDプロバイダへリダイレクトしています。"
}
}

View File

@@ -1,7 +0,0 @@
{
"LOGIN" : {
"INFO_OID_REDIRECT_PENDING" : "잠시만 기다려주십시오. ID 제공자로 리디렉션 중..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"INFO_OID_REDIRECT_PENDING" : "Por favor aguarde, redirecionando ao provedor de indentidade..."
}
}

View File

@@ -1,11 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "Бэкенд OpenID SSO"
},
"LOGIN" : {
"INFO_REDIRECT_PENDING" : "Пожалуйста, подождите. Переадресую на страницу аутентификации..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO后端"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"INFO_REDIRECT_PENDING" : "请稍候,正在重定向到身份提供者..."
}
}

View File

@@ -1,102 +0,0 @@
/*
* 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();
}
}

View File

@@ -1,12 +0,0 @@
{
"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 ..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_SAML" : {
"NAME" : "SAML Authentication Extension"
},
"LOGIN" : {
"FIELD_HEADER_SAML" : "",
"INFO_SAML_REDIRECT_PENDING" : "Please wait, redirecting to identity provider..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_SAML" : {
"NAME" : "SAML Authentication Extension"
},
"LOGIN" : {
"FIELD_HEADER_SAML" : "",
"INFO_SAML_REDIRECT_PENDING" : "Veuillez patienter, redirection vers le fournisseur d'identité..."
}
}

View File

@@ -1,12 +0,0 @@
{
"DATA_SOURCE_SAML" : {
"NAME" : "SAML Authentication Extension"
},
"LOGIN" : {
"FIELD_HEADER_SAML" : "",
"INFO_SAML_REDIRECT_PENDING" : "Por favor aguarde, redirecionando para o provedor de indentidade..."
}
}

View File

@@ -0,0 +1,2 @@
target/
*~

View File

@@ -0,0 +1 @@
src/main/resources/html/*.html

View File

@@ -0,0 +1,61 @@
<?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-base</artifactId>
<packaging>jar</packaging>
<name>guacamole-auth-sso-base</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>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<!-- JAX-RS Annotations -->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,180 @@
/*
* 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.sso;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.binder.LinkedBindingBuilder;
import java.util.Arrays;
import java.util.Collections;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.TokenInjectingUserContext;
import org.apache.guacamole.net.auth.UserContext;
/**
* An AuthenticationProvider which authenticates users against an arbitrary
* SSO system. Guice dependency injection is automatically configured via
* modules provided by the implementation. Implementations will typically
* provide no storage for connections, instead relying on other installed
* extensions.
*/
public abstract class SSOAuthenticationProvider extends AbstractAuthenticationProvider {
/**
* The Guice injector.
*/
private final Injector injector;
/**
* Creates a new SSOAuthenticationProvider that authenticates users against
* an arbitrary SSO system. Guice dependency injection is automatically
* configured, with the resulting injector available to implementations via
* {@link #getInjector()}. Core authentication functions are provided by
* the given SSOAuthenticationProviderService implementation, and
* additional implementation-specific services, providers, etc. may be
* bound by specifying additional Guice modules.
*
* @param authService
* The SSOAuthenticationProviderService implementation that should be
* used for core authentication functions.
*
* @param ssoResource
* The SSOResource that should be used to manually redirect the user to
* the IdP, as well as to provide any implementation-specific REST
* endpoints.
*
* @param modules
* Any additional modules that should be used when creating the Guice
* injector.
*/
public SSOAuthenticationProvider(
Class<? extends SSOAuthenticationProviderService> authService,
Class<? extends SSOResource> ssoResource,
Module... modules) {
this(authService, ssoResource, Arrays.asList(modules));
}
/**
* Creates a new SSOAuthenticationProvider that authenticates users against
* an arbitrary SSO system. Guice dependency injection is automatically
* configured, with the resulting injector available to implementations via
* {@link #getInjector()}. Core authentication functions are provided by
* the given SSOAuthenticationProviderService implementation, and
* additional may be provided by specifying additional Guice modules.
*
* @param authService
* The SSOAuthenticationProviderService implementation that should be
* used for core authentication functions.
*
* @param ssoResource
* The SSOResource that should be used to manually redirect the user to
* the IdP, as well as to provide any implementation-specific REST
* endpoints.
*
* @param modules
* Any additional modules that should be used when creating the Guice
* injector.
*/
public SSOAuthenticationProvider(
Class<? extends SSOAuthenticationProviderService> authService,
Class<? extends SSOResource> ssoResource,
Iterable<? extends Module> modules) {
injector = Guice.createInjector(Iterables.concat(Collections.singletonList(new AbstractModule() {
@Override
protected void configure() {
bind(AuthenticationProvider.class).toInstance(SSOAuthenticationProvider.this);
bind(Environment.class).toInstance(LocalEnvironment.getInstance());
bind(SSOAuthenticationProviderService.class).to(authService);
// Bind custom SSOResource implementation if different from
// core implementation (explicitly binding SSOResource as
// SSOResource results in a runtime error from Guice otherwise)
LinkedBindingBuilder<SSOResource> resourceBinding = bind(SSOResource.class);
if (ssoResource != SSOResource.class)
resourceBinding.to(ssoResource);
}
}), modules));
}
/**
* Returns the Guice injector available for use by this implementation of
* SSOAuthenticationProvider. The returned injector has already been
* configured with all modules supplied at the time this
* SSOAuthenticationProvider was created.
*
* @return
* The Guice injector available for use by this implementation of
* SSOAuthenticationProvider.
*/
protected final Injector getInjector() {
return injector;
}
@Override
public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
// Attempt to authenticate user with given credentials
SSOAuthenticationProviderService authProviderService =
injector.getInstance(SSOAuthenticationProviderService.class);
return authProviderService.authenticateUser(credentials);
}
@Override
public UserContext decorate(UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException {
// Only inject tokens for users authenticated by this extension
if (authenticatedUser.getAuthenticationProvider() != this)
return context;
return new TokenInjectingUserContext(context,
((SSOAuthenticatedUser) authenticatedUser).getTokens());
}
@Override
public SSOResource getResource() {
return getInjector().getInstance(SSOResource.class);
}
@Override
public void shutdown() {
injector.getInstance(SSOAuthenticationProviderService.class).shutdown();
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.sso;
import java.net.URI;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
/**
* Service that authenticates Guacamole users by leveraging an arbitrary SSO
* service.
*/
public interface SSOAuthenticationProviderService {
/**
* Returns an SSOAuthenticatedUser representing the user authenticated by
* the given credentials. Tokens associated with the returned
* SSOAuthenticatedUser will automatically be injected into any connections
* used by that user during their session.
*
* @param credentials
* The credentials to use for authentication.
*
* @return
* An SSOAuthenticatedUser representing the user authenticated by the
* given credentials.
*
* @throws GuacamoleException
* If an error occurs while authenticating the user, or if access is
* denied.
*/
SSOAuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException;
/**
* Returns the full URI of the login endpoint to which a user must be
* redirected in order to authenticate with the SSO identity provider.
*
* @return
* The full URI of the SSO login endpoint.
*
* @throws GuacamoleException
* If configuration information required for generating the login URI
* cannot be read.
*/
URI getLoginURI() throws GuacamoleException;
/**
* Frees all resources associated with the relevant
* SSOAuthenticationProvider implementation. This function is automatically
* invoked when an implementation of SSOAuthenticationProvider is shut
* down.
*/
void shutdown();
}

View File

@@ -0,0 +1,58 @@
/*
* 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.sso;
import com.google.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.apache.guacamole.GuacamoleException;
/**
* REST API resource that provides allows the user to be manually redirected to
* the applicable identity provider. Implementations may also provide
* additional resources and endpoints beneath this resource as needed.
*/
public class SSOResource {
/**
* Service for authenticating users using CAS.
*/
@Inject
private SSOAuthenticationProviderService authService;
/**
* Redirects the user to the relevant identity provider. If the SSO
* extension defining this resource is not the primary extension, and thus
* the user will not be automatically redirected to the IdP, this endpoint
* allows that redirect to occur manually upon a link/button click.
*
* @return
* An HTTP Response that will redirect the user to the IdP.
*
* @throws GuacamoleException
* If an error occurs preventing the redirect from being created.
*/
@GET
@Path("login")
public Response redirectToIdentityProvider() throws GuacamoleException {
return Response.seeOther(authService.getLoginURI()).build();
}
}

View File

@@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
package org.apache.guacamole.auth.cas.user; package org.apache.guacamole.auth.sso.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
@@ -28,11 +28,12 @@ import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
/** /**
* An CAS-specific implementation of AuthenticatedUser, associating a * An AuthenticatedUser whose identity has been supplied by an arbitrary SSO
* username and particular set of credentials with the CAS authentication * service. An SSOAuthenticatedUser may additionally be associated with a set
* provider. * of user-specific parameter tokens to be injected into any connections used
* by that user.
*/ */
public class CASAuthenticatedUser extends AbstractAuthenticatedUser { public class SSOAuthenticatedUser extends AbstractAuthenticatedUser {
/** /**
* Reference to the authentication provider associated with this * Reference to the authentication provider associated with this
@@ -45,60 +46,53 @@ public class CASAuthenticatedUser extends AbstractAuthenticatedUser {
* The credentials provided when this user was authenticated. * The credentials provided when this user was authenticated.
*/ */
private Credentials credentials; private Credentials credentials;
/**
* Tokens associated with this authenticated user.
*/
private Map<String, String> tokens;
/** /**
* The unique identifiers of all user groups which this user is a member of. * The groups that this user belongs to.
*/ */
private Set<String> effectiveGroups; private Set<String> effectiveGroups;
/** /**
* Initializes this AuthenticatedUser using the given username and * Parameter tokens to be automatically injected for any connections used
* credentials, and an empty map of parameter tokens. * by this user.
*
* @param username
* The username of the user that was authenticated.
*
* @param credentials
* The credentials provided when this user was authenticated.
*/ */
public void init(String username, Credentials credentials) { private Map<String, String> tokens;
this.init(username, credentials, Collections.emptyMap(), Collections.emptySet());
}
/** /**
* Initializes this AuthenticatedUser using the given username, * Initializes this SSOAuthenticatedUser, associating it with the given
* credentials, and parameter tokens. * username, credentials, groups, and parameter tokens. This function must
* be invoked for every SSOAuthenticatedUser created.
* *
* @param username * @param username
* The username of the user that was authenticated. * The username of the user that was authenticated.
* *
* @param credentials * @param credentials
* The credentials provided when this user was authenticated. * The credentials provided when this user was authenticated.
* *
* @param effectiveGroups
* The groups that the authenticated user belongs to.
*
* @param tokens * @param tokens
* A map of all the name/value pairs that should be available * A map of all the name/value pairs that should be available
* as tokens when connections are established with this user. * as tokens when connections are established by this user.
*/ */
public void init(String username, Credentials credentials, public void init(String username, Credentials credentials,
Map<String, String> tokens, Set<String> effectiveGroups) { Set<String> effectiveGroups, Map<String, String> tokens) {
this.credentials = credentials; this.credentials = credentials;
this.effectiveGroups = Collections.unmodifiableSet(effectiveGroups);
this.tokens = Collections.unmodifiableMap(tokens); this.tokens = Collections.unmodifiableMap(tokens);
this.effectiveGroups = effectiveGroups; setIdentifier(username);
setIdentifier(username.toLowerCase());
} }
/** /**
* Returns a Map containing the name/value pairs that can be applied * Returns a Map of the parameter tokens that should be automatically
* as parameter tokens when connections are established by the user. * injected into connections used by this user during their session. If
* * there are no parameter tokens applicable to the SSO implementation, this
* may simply be an empty map.
*
* @return * @return
* A Map containing all of the name/value pairs that can be * A map of the parameter token name/value pairs that should be
* used as parameter tokens by this user. * automatically injected into connections used by this user.
*/ */
public Map<String, String> getTokens() { public Map<String, String> getTokens() {
return tokens; return tokens;

View File

@@ -0,0 +1,7 @@
<meta name="after" content=".login-ui .login-dialog-middle">
<div class="sso-providers">
<div class="sso-providers-content">
{{ 'LOGIN.SECTION_HEADER_SSO_OPTIONS' | translate }}
<ul class="sso-provider-list"></ul>
</div>
</div>

View File

@@ -0,0 +1,50 @@
/*
* 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.
*/
.login-ui .sso-providers {
display: none;
}
.login-ui .sso-providers:last-child {
display: table-row;
}
.sso-providers ul {
list-style: none;
}
.sso-providers ul, .sso-providers li {
display: inline-block;
margin: 0;
padding: 0;
}
.sso-providers li::before {
content: ' / ';
}
.sso-providers li:first-child::before {
display: none;
}
.sso-providers-content {
display: table-cell;
padding: 0.25em 0.5em;
height: 1px;
}

View File

@@ -0,0 +1,15 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "Backend d'inici de sessió unificat (SSO) CAS"
},
"DATA_SOURCE_SAML" : {
"NAME" : "Extensión de autenticación SAML"
},
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "Espereu, redirigint al proveïdor d'identitat ..."
}
}

View File

@@ -1,7 +1,7 @@
{ {
"LOGIN" : { "LOGIN" : {
"INFO_OID_REDIRECT_PENDING" : "Bitte warten, Sie werden zum Identitätsprovider weitergeleitet..." "INFO_IDP_REDIRECT_PENDING" : "Bitte warten, Sie werden zum Identitätsprovider weitergeleitet..."
} }
} }

View File

@@ -0,0 +1,26 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO Backend"
},
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO Backend"
},
"DATA_SOURCE_SAML" : {
"NAME" : "SAML SSO Backend"
},
"LOGIN" : {
"FIELD_HEADER_ID_TOKEN" : "",
"FIELD_HEADER_STATE" : "",
"FIELD_HEADER_TICKET" : "",
"INFO_IDP_REDIRECT_PENDING" : "Please wait, redirecting to identity provider...",
"NAME_IDP_CAS" : "CAS",
"NAME_IDP_OPENID" : "OpenID",
"NAME_IDP_SAML" : "SAML",
"SECTION_HEADER_SSO_OPTIONS" : "Sign in with:"
}
}

View File

@@ -0,0 +1,7 @@
{
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "Veuillez patienter, redirection vers le fournisseur d'identité..."
}
}

View File

@@ -0,0 +1,7 @@
{
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "IDプロバイダへリダイレクトしています。"
}
}

View File

@@ -5,7 +5,7 @@
}, },
"LOGIN" : { "LOGIN" : {
"INFO_SAML_REDIRECT_PENDING": "잠시만 기다려주십시오. ID 제공자로 리디렉션 중..." "INFO_IDP_REDIRECT_PENDING" : "잠시만 기다려주십시오. ID 제공자로 리디렉션 중..."
} }
} }

View File

@@ -0,0 +1,7 @@
{
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "Por favor aguarde, redirecionando ao provedor de indentidade..."
}
}

View File

@@ -0,0 +1,15 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "Бэкенд CAS SSO"
},
"DATA_SOURCE_OPENID" : {
"NAME" : "Бэкенд OpenID SSO"
},
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "Пожалуйста, подождите. Переадресую на страницу аутентификации..."
}
}

View File

@@ -0,0 +1,15 @@
{
"DATA_SOURCE_CAS" : {
"NAME" : "CAS SSO后端"
},
"DATA_SOURCE_OPENID" : {
"NAME" : "OpenID SSO后端"
},
"LOGIN" : {
"INFO_IDP_REDIRECT_PENDING" : "请稍候,正在重定向到身份提供者..."
}
}

View File

@@ -0,0 +1 @@
src/main/resources/html/*.html

View File

@@ -24,17 +24,17 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-cas</artifactId> <artifactId>guacamole-auth-sso-cas</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.3.0</version> <version>1.3.0</version>
<name>guacamole-auth-cas</name> <name>guacamole-auth-sso-cas</name>
<url>http://guacamole.apache.org/</url> <url>http://guacamole.apache.org/</url>
<parent> <parent>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>extensions</artifactId> <artifactId>guacamole-auth-sso</artifactId>
<version>1.3.0</version> <version>1.3.0</version>
<relativePath>../</relativePath> <relativePath>../../</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@@ -43,8 +43,12 @@
<dependency> <dependency>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId> <artifactId>guacamole-ext</artifactId>
<version>1.3.0</version> </dependency>
<scope>provided</scope>
<!-- Core SSO support -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-base</artifactId>
</dependency> </dependency>
<!-- Apereo CAS Client API --> <!-- Apereo CAS Client API -->
@@ -70,16 +74,12 @@
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- Jersey - JAX-RS Implementation --> <!-- JAX-RS Annotations -->
<dependency> <dependency>
<groupId>javax.ws.rs</groupId> <groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId> <artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- JUnit --> <!-- JUnit -->

View File

@@ -20,24 +20,38 @@
package org.apache.guacamole.auth.cas; package org.apache.guacamole.auth.cas;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.form.Field; import org.apache.guacamole.form.Field;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
import org.apache.guacamole.auth.cas.conf.ConfigurationService; import org.apache.guacamole.auth.cas.conf.ConfigurationService;
import org.apache.guacamole.auth.cas.form.CASTicketField;
import org.apache.guacamole.auth.cas.ticket.TicketValidationService; import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser; import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableMessage; import org.apache.guacamole.language.TranslatableMessage;
/** /**
* Service providing convenience functions for the CAS AuthenticationProvider * Service that authenticates Guacamole users by processing CAS tickets.
* implementation.
*/ */
public class AuthenticationProviderService { @Singleton
public class AuthenticationProviderService implements SSOAuthenticationProviderService {
/**
* The parameter that will be present upon successful CAS authentication.
*/
public static final String TICKET_PARAMETER_NAME = "ticket";
/**
* The standard URI name for the CAS login resource.
*/
private static final String CAS_LOGIN_URI = "login";
/** /**
* Service for retrieving CAS configuration information. * Service for retrieving CAS configuration information.
@@ -51,48 +65,42 @@ public class AuthenticationProviderService {
@Inject @Inject
private TicketValidationService ticketService; private TicketValidationService ticketService;
/** @Override
* Returns an AuthenticatedUser representing the user authenticated by the public SSOAuthenticatedUser authenticateUser(Credentials credentials)
* given credentials.
*
* @param credentials
* The credentials to use for authentication.
*
* @return
* A CASAuthenticatedUser representing the user authenticated by the
* given credentials.
*
* @throws GuacamoleException
* If an error occurs while authenticating the user, or if access is
* denied.
*/
public CASAuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
// Pull CAS ticket from request if present // Pull CAS ticket from request if present
HttpServletRequest request = credentials.getRequest(); HttpServletRequest request = credentials.getRequest();
if (request != null) { if (request != null) {
String ticket = request.getParameter(CASTicketField.PARAMETER_NAME); String ticket = request.getParameter(TICKET_PARAMETER_NAME);
if (ticket != null) { if (ticket != null) {
return ticketService.validateTicket(ticket, credentials); return ticketService.validateTicket(ticket, credentials);
} }
} }
// Request CAS ticket // Request CAS ticket (will automatically redirect the user to the
// CAS authorization page via JavaScript)
throw new GuacamoleInvalidCredentialsException("Invalid login.", throw new GuacamoleInvalidCredentialsException("Invalid login.",
new CredentialsInfo(Arrays.asList(new Field[] { new CredentialsInfo(Arrays.asList(new Field[] {
new RedirectField(TICKET_PARAMETER_NAME, getLoginURI(),
// CAS-specific ticket (will automatically redirect the user new TranslatableMessage("LOGIN.INFO_IDP_REDIRECT_PENDING"))
// to the authorization page via JavaScript)
new CASTicketField(
confService.getAuthorizationEndpoint(),
confService.getRedirectURI(),
new TranslatableMessage("LOGIN.INFO_CAS_REDIRECT_PENDING")
)
})) }))
); );
} }
@Override
public URI getLoginURI() throws GuacamoleException {
return UriBuilder.fromUri(confService.getAuthorizationEndpoint())
.path(CAS_LOGIN_URI)
.queryParam("service", confService.getRedirectURI())
.build();
}
@Override
public void shutdown() {
// Nothing to clean up
}
} }

View File

@@ -0,0 +1,47 @@
/*
* 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.cas;
import org.apache.guacamole.auth.sso.SSOAuthenticationProvider;
import org.apache.guacamole.auth.sso.SSOResource;
/**
* Guacamole authentication backend which authenticates users using an
* arbitrary external system implementing CAS. No storage for connections is
* provided - only authentication. Storage must be provided by some other
* extension.
*/
public class CASAuthenticationProvider extends SSOAuthenticationProvider {
/**
* Creates a new CASAuthenticationProvider that authenticates users
* against an CAS service
*/
public CASAuthenticationProvider() {
super(AuthenticationProviderService.class,
SSOResource.class, new CASAuthenticationProviderModule());
}
@Override
public String getIdentifier() {
return "cas";
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cas;
import com.google.inject.AbstractModule;
import org.apache.guacamole.auth.cas.conf.ConfigurationService;
import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
/**
* Guice module which configures CAS-specific injections.
*/
public class CASAuthenticationProviderModule extends AbstractModule {
@Override
protected void configure() {
bind(ConfigurationService.class);
bind(TicketValidationService.class);
}
}

View File

@@ -41,7 +41,7 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.cas.conf.ConfigurationService; import org.apache.guacamole.auth.cas.conf.ConfigurationService;
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser; import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.token.TokenName; import org.apache.guacamole.token.TokenName;
import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.authentication.AttributePrincipal;
@@ -77,7 +77,7 @@ public class TicketValidationService {
* Provider for AuthenticatedUser objects. * Provider for AuthenticatedUser objects.
*/ */
@Inject @Inject
private Provider<CASAuthenticatedUser> authenticatedUserProvider; private Provider<SSOAuthenticatedUser> authenticatedUserProvider;
/** /**
* Converts the given CAS attribute value object (whose type is variable) * Converts the given CAS attribute value object (whose type is variable)
@@ -132,7 +132,7 @@ public class TicketValidationService {
* If the ID ticket is not valid or guacamole.properties could * If the ID ticket is not valid or guacamole.properties could
* not be parsed. * not be parsed.
*/ */
public CASAuthenticatedUser validateTicket(String ticket, public SSOAuthenticatedUser validateTicket(String ticket,
Credentials credentials) throws GuacamoleException { Credentials credentials) throws GuacamoleException {
// Create a ticket validator that uses the configured CAS URL // Create a ticket validator that uses the configured CAS URL
@@ -160,6 +160,9 @@ public class TicketValidationService {
if (username == null) if (username == null)
throw new GuacamoleSecurityException("No username provided by CAS."); throw new GuacamoleSecurityException("No username provided by CAS.");
// Canonicalize username as lowercase
username = username.toLowerCase();
// Update credentials with username provided by CAS for sake of // Update credentials with username provided by CAS for sake of
// ${GUAC_USERNAME} token // ${GUAC_USERNAME} token
credentials.setUsername(username); credentials.setUsername(username);
@@ -196,8 +199,8 @@ public class TicketValidationService {
} }
}); });
CASAuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); SSOAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(username, credentials, tokens, effectiveGroups); authenticatedUser.init(username, credentials, effectiveGroups, tokens);
return authenticatedUser; return authenticatedUser;
} }

View File

@@ -9,6 +9,15 @@
"org.apache.guacamole.auth.cas.CASAuthenticationProvider" "org.apache.guacamole.auth.cas.CASAuthenticationProvider"
], ],
"css" : [
"styles/sso-providers.css"
],
"html" : [
"html/sso-providers.html",
"html/sso-provider-cas.html"
],
"translations" : [ "translations" : [
"translations/ca.json", "translations/ca.json",
"translations/de.json", "translations/de.json",

View File

@@ -0,0 +1,4 @@
<meta name="after-children" content=".login-ui .sso-provider-list:last-child">
<li class="sso-provider sso-provider-cas"><a href="api/ext/cas/login">{{
'LOGIN.NAME_IDP_CAS' | translate
}}</a></li>

View File

@@ -0,0 +1,72 @@
<?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-dist</artifactId>
<packaging>pom</packaging>
<name>guacamole-auth-sso-dist</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>
<!-- CAS Authentication Extension -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-cas</artifactId>
<version>1.3.0</version>
</dependency>
<!-- OpenID Authentication Extension -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-openid</artifactId>
<version>1.3.0</version>
</dependency>
<!-- SAML Authentication Extension -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-saml</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
<build>
<!-- Dist .tar.gz for guacamole-auth-sso should be named after the
parent guacamole-auth-sso project, not after guacamole-auth-sso-dist -->
<finalName>${project.parent.artifactId}-${project.parent.version}</finalName>
</build>
</project>

View File

@@ -21,33 +21,53 @@
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> 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 --> <id>dist</id>
<baseDirectory>${project.parent.artifactId}-${project.parent.version}</baseDirectory>
<!-- Output .tar.gz -->
<formats> <formats>
<format>tar.gz</format> <format>tar.gz</format>
</formats> </formats>
<!-- Include licenses and extension .jar --> <!-- Include extension .jars -->
<fileSets> <dependencySets>
<!-- Include licenses --> <!-- CAS extension .jar -->
<dependencySet>
<outputDirectory>cas</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
<includes>
<include>org.apache.guacamole:guacamole-auth-sso-cas</include>
</includes>
</dependencySet>
<!-- OpenID extension .jar -->
<dependencySet>
<outputDirectory>openid</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
<includes>
<include>org.apache.guacamole:guacamole-auth-sso-openid</include>
</includes>
</dependencySet>
<!-- SAML extension .jar -->
<dependencySet>
<outputDirectory>saml</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
<includes>
<include>org.apache.guacamole:guacamole-auth-sso-saml</include>
</includes>
</dependencySet>
</dependencySets>
<!-- Include extension licenses -->
<fileSets>
<fileSet> <fileSet>
<outputDirectory></outputDirectory> <outputDirectory></outputDirectory>
<directory>target/licenses</directory> <directory>target/licenses</directory>
</fileSet> </fileSet>
<!-- Include extension .jar -->
<fileSet>
<directory>target</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets> </fileSets>
</assembly> </assembly>

View File

@@ -0,0 +1 @@
src/main/resources/html/*.html

View File

@@ -24,17 +24,17 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-openid</artifactId> <artifactId>guacamole-auth-sso-openid</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.3.0</version> <version>1.3.0</version>
<name>guacamole-auth-openid</name> <name>guacamole-auth-sso-openid</name>
<url>http://guacamole.apache.org/</url> <url>http://guacamole.apache.org/</url>
<parent> <parent>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>extensions</artifactId> <artifactId>guacamole-auth-sso</artifactId>
<version>1.3.0</version> <version>1.3.0</version>
<relativePath>../</relativePath> <relativePath>../../</relativePath>
</parent> </parent>
<build> <build>
@@ -94,8 +94,12 @@
<dependency> <dependency>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId> <artifactId>guacamole-ext</artifactId>
<version>1.3.0</version> </dependency>
<scope>provided</scope>
<!-- Core SSO support -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-base</artifactId>
</dependency> </dependency>
<!-- Java implementation of JOSE (jose.4.j) --> <!-- Java implementation of JOSE (jose.4.j) -->
@@ -115,16 +119,12 @@
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- Jersey - JAX-RS Implementation --> <!-- JAX-RS Annotations -->
<dependency> <dependency>
<groupId>javax.ws.rs</groupId> <groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId> <artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -21,34 +21,38 @@ package org.apache.guacamole.auth.openid;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.auth.openid.conf.ConfigurationService; import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.form.TokenField;
import org.apache.guacamole.auth.openid.token.NonceService; import org.apache.guacamole.auth.openid.token.NonceService;
import org.apache.guacamole.auth.openid.token.TokenValidationService; import org.apache.guacamole.auth.openid.token.TokenValidationService;
import org.apache.guacamole.auth.openid.user.AuthenticatedUser;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.form.Field; import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableMessage; import org.apache.guacamole.language.TranslatableMessage;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.JwtClaims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Service providing convenience functions for the OpenID AuthenticationProvider * Service that authenticates Guacamole users by processing OpenID tokens.
* implementation.
*/ */
public class AuthenticationProviderService { @Singleton
public class AuthenticationProviderService implements SSOAuthenticationProviderService {
/** /**
* Logger for this class. * The standard HTTP parameter which will be included within the URL by all
* OpenID services upon successful authentication and redirect.
*/ */
private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); public static final String TOKEN_PARAMETER_NAME = "id_token";
/** /**
* Service for retrieving OpenID configuration information. * Service for retrieving OpenID configuration information.
@@ -72,24 +76,10 @@ public class AuthenticationProviderService {
* Provider for AuthenticatedUser objects. * Provider for AuthenticatedUser objects.
*/ */
@Inject @Inject
private Provider<AuthenticatedUser> authenticatedUserProvider; private Provider<SSOAuthenticatedUser> authenticatedUserProvider;
/** @Override
* Returns an AuthenticatedUser representing the user authenticated by the public SSOAuthenticatedUser authenticateUser(Credentials credentials)
* 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 { throws GuacamoleException {
String username = null; String username = null;
@@ -98,7 +88,7 @@ public class AuthenticationProviderService {
// Validate OpenID token in request, if present, and derive username // Validate OpenID token in request, if present, and derive username
HttpServletRequest request = credentials.getRequest(); HttpServletRequest request = credentials.getRequest();
if (request != null) { if (request != null) {
String token = request.getParameter(TokenField.PARAMETER_NAME); String token = request.getParameter(TOKEN_PARAMETER_NAME);
if (token != null) { if (token != null) {
JwtClaims claims = tokenService.validateToken(token); JwtClaims claims = tokenService.validateToken(token);
if (claims != null) { if (claims != null) {
@@ -113,30 +103,37 @@ public class AuthenticationProviderService {
if (username != null) { if (username != null) {
// Create corresponding authenticated user // Create corresponding authenticated user
AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); SSOAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(username, credentials, groups); authenticatedUser.init(username, credentials, groups, Collections.emptyMap());
return authenticatedUser; return authenticatedUser;
} }
// Request OpenID token // Request OpenID token (will automatically redirect the user to the
// OpenID authorization page via JavaScript)
throw new GuacamoleInvalidCredentialsException("Invalid login.", throw new GuacamoleInvalidCredentialsException("Invalid login.",
new CredentialsInfo(Arrays.asList(new Field[] { new CredentialsInfo(Arrays.asList(new Field[] {
new RedirectField(TOKEN_PARAMETER_NAME, getLoginURI(),
// OpenID-specific token (will automatically redirect the user new TranslatableMessage("LOGIN.INFO_IDP_REDIRECT_PENDING"))
// to the authorization page via JavaScript)
new TokenField(
confService.getAuthorizationEndpoint(),
confService.getScope(),
confService.getClientID(),
confService.getRedirectURI(),
nonceService.generate(confService.getMaxNonceValidity() * 60000L),
new TranslatableMessage("LOGIN.INFO_OID_REDIRECT_PENDING")
)
})) }))
); );
} }
@Override
public URI getLoginURI() throws GuacamoleException {
return UriBuilder.fromUri(confService.getAuthorizationEndpoint())
.queryParam("scope", confService.getScope())
.queryParam("response_type", "id_token")
.queryParam("client_id", confService.getClientID())
.queryParam("redirect_uri", confService.getRedirectURI())
.queryParam("nonce", nonceService.generate(confService.getMaxNonceValidity() * 60000L))
.build();
}
@Override
public void shutdown() {
// Nothing to clean up
}
} }

View File

@@ -19,12 +19,8 @@
package org.apache.guacamole.auth.openid; package org.apache.guacamole.auth.openid;
import com.google.inject.Guice; import org.apache.guacamole.auth.sso.SSOAuthenticationProvider;
import com.google.inject.Injector; import org.apache.guacamole.auth.sso.SSOResource;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
/** /**
* Guacamole authentication backend which authenticates users using an * Guacamole authentication backend which authenticates users using an
@@ -32,29 +28,15 @@ import org.apache.guacamole.net.auth.Credentials;
* provided - only authentication. Storage must be provided by some other * provided - only authentication. Storage must be provided by some other
* extension. * extension.
*/ */
public class OpenIDAuthenticationProvider extends AbstractAuthenticationProvider { public class OpenIDAuthenticationProvider extends SSOAuthenticationProvider {
/**
* Injector which will manage the object graph of this authentication
* provider.
*/
private final Injector injector;
/** /**
* Creates a new OpenIDAuthenticationProvider that authenticates users * Creates a new OpenIDAuthenticationProvider that authenticates users
* against an OpenID service. * against an OpenID service.
*
* @throws GuacamoleException
* If a required property is missing, or an error occurs while parsing
* a property.
*/ */
public OpenIDAuthenticationProvider() throws GuacamoleException { public OpenIDAuthenticationProvider() {
super(AuthenticationProviderService.class, SSOResource.class,
// Set up Guice injector. new OpenIDAuthenticationProviderModule());
injector = Guice.createInjector(
new OpenIDAuthenticationProviderModule(this)
);
} }
@Override @Override
@@ -62,14 +44,4 @@ public class OpenIDAuthenticationProvider extends AbstractAuthenticationProvider
return "openid"; return "openid";
} }
@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);
}
} }

View File

@@ -0,0 +1,39 @@
/*
* 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.openid;
import com.google.inject.AbstractModule;
import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.token.NonceService;
import org.apache.guacamole.auth.openid.token.TokenValidationService;
/**
* Guice module which configures OpenID-specific injections.
*/
public class OpenIDAuthenticationProviderModule extends AbstractModule {
@Override
protected void configure() {
bind(ConfigurationService.class);
bind(NonceService.class);
bind(TokenValidationService.class);
}
}

View File

@@ -9,6 +9,15 @@
"org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider" "org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider"
], ],
"css" : [
"styles/sso-providers.css"
],
"html" : [
"html/sso-providers.html",
"html/sso-provider-openid.html"
],
"translations" : [ "translations" : [
"translations/ca.json", "translations/ca.json",
"translations/de.json", "translations/de.json",

View File

@@ -0,0 +1,4 @@
<meta name="after-children" content=".login-ui .sso-provider-list:last-child">
<li class="sso-provider sso-provider-openid"><a href="api/ext/openid/login">{{
'LOGIN.NAME_IDP_OPENID' | translate
}}</a></li>

View File

@@ -0,0 +1 @@
src/main/resources/html/*.html

View File

@@ -24,17 +24,17 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-saml</artifactId> <artifactId>guacamole-auth-sso-saml</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.3.0</version> <version>1.3.0</version>
<name>guacamole-auth-saml</name> <name>guacamole-auth-sso-saml</name>
<url>http://guacamole.apache.org/</url> <url>http://guacamole.apache.org/</url>
<parent> <parent>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>extensions</artifactId> <artifactId>guacamole-auth-sso</artifactId>
<version>1.3.0</version> <version>1.3.0</version>
<relativePath>../</relativePath> <relativePath>../../</relativePath>
</parent> </parent>
<dependencies> <dependencies>
@@ -43,8 +43,12 @@
<dependency> <dependency>
<groupId>org.apache.guacamole</groupId> <groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId> <artifactId>guacamole-ext</artifactId>
<version>1.3.0</version> </dependency>
<scope>provided</scope>
<!-- Core SSO support -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-sso-base</artifactId>
</dependency> </dependency>
<!-- Guice --> <!-- Guice -->
@@ -57,16 +61,12 @@
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- Jersey - JAX-RS Implementation --> <!-- JAX-RS Annotations -->
<dependency> <dependency>
<groupId>javax.ws.rs</groupId> <groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId> <artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- OneLogin SAML Library --> <!-- OneLogin SAML Library -->

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.saml;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.net.URI; import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@@ -29,19 +30,20 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.saml.acs.AssertedIdentity; import org.apache.guacamole.auth.saml.acs.AssertedIdentity;
import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager;
import org.apache.guacamole.auth.saml.acs.SAMLService; import org.apache.guacamole.auth.saml.acs.SAMLService;
import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
import org.apache.guacamole.form.Field; import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.RedirectField; import org.apache.guacamole.form.RedirectField;
import org.apache.guacamole.language.TranslatableMessage; 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;
import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
/** /**
* Service that authenticates Guacamole users by processing the responses of * Service that authenticates Guacamole users by processing the responses of
* SAML identity providers. * SAML identity providers.
*/ */
public class AuthenticationProviderService { @Singleton
public class AuthenticationProviderService implements SSOAuthenticationProviderService {
/** /**
* The name of the query parameter that identifies an active authentication * The name of the query parameter that identifies an active authentication
@@ -67,22 +69,8 @@ public class AuthenticationProviderService {
@Inject @Inject
private SAMLService saml; private SAMLService saml;
/** @Override
* Returns an AuthenticatedUser representing the user authenticated by the public SAMLAuthenticatedUser authenticateUser(Credentials credentials)
* 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 { throws GuacamoleException {
// No authentication can be attempted without a corresponding HTTP // No authentication can be attempted without a corresponding HTTP
@@ -107,13 +95,23 @@ public class AuthenticationProviderService {
// Redirect to SAML IdP if no SAML identity is associated with the // Redirect to SAML IdP if no SAML identity is associated with the
// Guacamole authentication request // Guacamole authentication request
URI authUri = saml.createRequest(); throw new GuacamoleInvalidCredentialsException("Redirecting to SAML IdP.",
throw new GuacamoleInsufficientCredentialsException("Redirecting to SAML IdP.",
new CredentialsInfo(Arrays.asList(new Field[] { new CredentialsInfo(Arrays.asList(new Field[] {
new RedirectField("samlRedirect", authUri, new TranslatableMessage("LOGIN.INFO_SAML_REDIRECT_PENDING")) new RedirectField(AUTH_SESSION_QUERY_PARAM, getLoginURI(),
new TranslatableMessage("LOGIN.INFO_IDP_REDIRECT_PENDING"))
})) }))
); );
} }
@Override
public URI getLoginURI() throws GuacamoleException {
return saml.createRequest();
}
@Override
public void shutdown() {
sessionManager.shutdown();
}
} }

View File

@@ -0,0 +1,48 @@
/*
* 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 org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource;
import org.apache.guacamole.auth.sso.SSOAuthenticationProvider;
/**
* 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 SSOAuthenticationProvider {
/**
* Creates a new SAMLAuthenticationProvider that authenticates users
* against a SAML IdP.
*/
public SAMLAuthenticationProvider() {
super(AuthenticationProviderService.class,
AssertionConsumerServiceResource.class,
new SAMLAuthenticationProviderModule());
}
@Override
public String getIdentifier() {
return "saml";
}
}

View File

@@ -25,57 +25,19 @@ import org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource;
import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager;
import org.apache.guacamole.auth.saml.acs.IdentifierGenerator; import org.apache.guacamole.auth.saml.acs.IdentifierGenerator;
import org.apache.guacamole.auth.saml.acs.SAMLService; 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. * Guice module which configures SAML-specific injections.
*/ */
public class SAMLAuthenticationProviderModule extends AbstractModule { 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 @Override
protected void configure() { 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(AssertionConsumerServiceResource.class);
bind(AuthenticationSessionManager.class); bind(AuthenticationSessionManager.class);
bind(ConfigurationService.class); bind(ConfigurationService.class);
bind(IdentifierGenerator.class); bind(IdentifierGenerator.class);
bind(SAMLService.class); bind(SAMLService.class);
} }
} }

View File

@@ -30,6 +30,7 @@ import javax.ws.rs.core.UriBuilder;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.saml.AuthenticationProviderService; import org.apache.guacamole.auth.saml.AuthenticationProviderService;
import org.apache.guacamole.auth.saml.conf.ConfigurationService; import org.apache.guacamole.auth.saml.conf.ConfigurationService;
import org.apache.guacamole.auth.sso.SSOResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -38,7 +39,7 @@ import org.slf4j.LoggerFactory;
* endpoint. SAML identity providers will issue an HTTP POST to this endpoint * endpoint. SAML identity providers will issue an HTTP POST to this endpoint
* asserting the user's identity when the user has successfully authenticated. * asserting the user's identity when the user has successfully authenticated.
*/ */
public class AssertionConsumerServiceResource { public class AssertionConsumerServiceResource extends SSOResource {
/** /**
* Logger for this class. * Logger for this class.

View File

@@ -29,8 +29,7 @@ import java.util.stream.Collectors;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.saml.acs.AssertedIdentity; import org.apache.guacamole.auth.saml.acs.AssertedIdentity;
import org.apache.guacamole.auth.saml.conf.ConfigurationService; import org.apache.guacamole.auth.saml.conf.ConfigurationService;
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.token.TokenName; import org.apache.guacamole.token.TokenName;
@@ -39,7 +38,7 @@ import org.apache.guacamole.token.TokenName;
* identity and particular set of credentials with the SAML authentication * identity and particular set of credentials with the SAML authentication
* provider. * provider.
*/ */
public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser { public class SAMLAuthenticatedUser extends SSOAuthenticatedUser {
/** /**
* The prefix that should be prepended to all parameter tokens generated * The prefix that should be prepended to all parameter tokens generated
@@ -53,28 +52,6 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
@Inject @Inject
private ConfigurationService confService; 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 * Returns a Map of all parameter tokens that should be made available for
* substitution based on the given {@link AssertedIdentity}. The resulting * substitution based on the given {@link AssertedIdentity}. The resulting
@@ -144,35 +121,7 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
*/ */
public void init(AssertedIdentity identity, Credentials credentials) public void init(AssertedIdentity identity, Credentials credentials)
throws GuacamoleException { throws GuacamoleException {
this.credentials = credentials; super.init(identity.getUsername(), credentials, getGroups(identity), getTokens(identity));
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;
}
} }

View File

@@ -9,12 +9,25 @@
"org.apache.guacamole.auth.saml.SAMLAuthenticationProvider" "org.apache.guacamole.auth.saml.SAMLAuthenticationProvider"
], ],
"css" : [
"styles/sso-providers.css"
],
"html" : [
"html/sso-providers.html",
"html/sso-provider-saml.html"
],
"translations" : [ "translations" : [
"translations/ca.json", "translations/ca.json",
"translations/de.json",
"translations/en.json", "translations/en.json",
"translations/ko.json",
"translations/fr.json", "translations/fr.json",
"translations/pt.json" "translations/ja.json",
"translations/ko.json",
"translations/pt.json",
"translations/ru.json",
"translations/zh.json"
] ]
} }

Some files were not shown because too many files have changed in this diff Show More