GUACAMOLE-210: Refactor source referencing OAuth to OpenID. This extension uses OpenID, not OAuth.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/d04d6122 Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/d04d6122 Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/d04d6122 Branch: refs/heads/master Commit: d04d61225a9f820b99fd1815c5b24205dc1cc8e1 Parents: 1034612 Author: Michael Jumper <[email protected]> Authored: Tue Feb 21 12:43:15 2017 -0800 Committer: Michael Jumper <[email protected]> Committed: Mon Sep 25 13:06:44 2017 -0700 ---------------------------------------------------------------------- .../oauth/AuthenticationProviderService.java | 119 ---------------- .../auth/oauth/OAuthAuthenticationProvider.java | 105 -------------- .../OAuthAuthenticationProviderModule.java | 81 ----------- .../auth/oauth/conf/ConfigurationService.java | 139 ------------------ .../oauth/conf/OAuthGuacamoleProperties.java | 108 -------------- .../auth/oauth/form/OAuthTokenField.java | 118 ---------------- .../oauth/token/TokenValidationService.java | 105 -------------- .../auth/oauth/user/AuthenticatedUser.java | 71 ---------- .../openid/AuthenticationProviderService.java | 119 ++++++++++++++++ .../openid/OpenIDAuthenticationProvider.java | 105 ++++++++++++++ .../OpenIDAuthenticationProviderModule.java | 81 +++++++++++ .../auth/openid/conf/ConfigurationService.java | 140 +++++++++++++++++++ .../openid/conf/OpenIDGuacamoleProperties.java | 108 ++++++++++++++ .../guacamole/auth/openid/form/TokenField.java | 118 ++++++++++++++++ .../openid/token/TokenValidationService.java | 105 ++++++++++++++ .../auth/openid/user/AuthenticatedUser.java | 71 ++++++++++ .../src/main/resources/guac-manifest.json | 12 +- .../src/main/resources/oauthConfig.js | 54 ------- .../src/main/resources/oauthController.js | 30 ---- .../src/main/resources/oauthModule.js | 28 ---- .../src/main/resources/openidConfig.js | 54 +++++++ .../src/main/resources/openidController.js | 30 ++++ .../src/main/resources/openidModule.js | 28 ++++ 23 files changed, 965 insertions(+), 964 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java deleted file mode 100644 index d89f087..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java +++ /dev/null @@ -1,119 +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.oauth; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import java.util.Arrays; -import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.auth.oauth.form.OAuthTokenField; -import org.apache.guacamole.auth.oauth.token.TokenValidationService; -import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.form.Field; -import org.apache.guacamole.net.auth.Credentials; -import org.apache.guacamole.net.auth.credentials.CredentialsInfo; -import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Service providing convenience functions for the OAuth AuthenticationProvider - * implementation. - */ -public class AuthenticationProviderService { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); - - /** - * Service for retrieving OAuth configuration information. - */ - @Inject - private ConfigurationService confService; - - /** - * Service for validating received ID tokens. - */ - @Inject - private TokenValidationService tokenService; - - /** - * Provider for AuthenticatedUser objects. - */ - @Inject - private Provider<AuthenticatedUser> authenticatedUserProvider; - - /** - * Returns an AuthenticatedUser representing the user authenticated by the - * given credentials. - * - * @param credentials - * The credentials to use for authentication. - * - * @return - * An AuthenticatedUser representing the user authenticated by the - * given credentials. - * - * @throws GuacamoleException - * If an error occurs while authenticating the user, or if access is - * denied. - */ - public AuthenticatedUser authenticateUser(Credentials credentials) - throws GuacamoleException { - - String token = null; - - // Pull OAuth token from request if present - HttpServletRequest request = credentials.getRequest(); - if (request != null) - token = request.getParameter(OAuthTokenField.PARAMETER_NAME); - - // If token provided, validate and produce authenticated user - if (token != null) { - - // Create corresponding authenticated user - AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(tokenService.processUsername(token), credentials); - return authenticatedUser; - - } - - // Request OAuth token - throw new GuacamoleInvalidCredentialsException("Invalid login.", - new CredentialsInfo(Arrays.asList(new Field[] { - - // OAuth-specific token (will automatically redirect the user - // to the authorization page via JavaScript) - new OAuthTokenField( - confService.getAuthorizationEndpoint(), - confService.getClientID(), - confService.getRedirectURI() - ) - - })) - ); - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java deleted file mode 100644 index 6ecfeb5..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java +++ /dev/null @@ -1,105 +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.oauth; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import org.apache.guacamole.GuacamoleException; -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.UserContext; - -/** - * Guacamole authentication backend which authenticates users using an - * arbitrary external system implementing OAuth. No storage for connections is - * provided - only authentication. Storage must be provided by some other - * extension. - */ -public class OAuthAuthenticationProvider implements AuthenticationProvider { - - /** - * Injector which will manage the object graph of this authentication - * provider. - */ - private final Injector injector; - - /** - * Creates a new OAuthAuthenticationProvider that authenticates users - * against an OAuth service - * - * @throws GuacamoleException - * If a required property is missing, or an error occurs while parsing - * a property. - */ - public OAuthAuthenticationProvider() throws GuacamoleException { - - // Set up Guice injector. - injector = Guice.createInjector( - new OAuthAuthenticationProviderModule(this) - ); - - } - - @Override - public String getIdentifier() { - return "oauth"; - } - - @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 AuthenticatedUser updateAuthenticatedUser( - AuthenticatedUser authenticatedUser, Credentials credentials) - throws GuacamoleException { - - // No update necessary - return authenticatedUser; - - } - - @Override - public UserContext getUserContext(AuthenticatedUser authenticatedUser) - throws GuacamoleException { - - // No associated data whatsoever - return null; - - } - - @Override - public UserContext updateUserContext(UserContext context, - AuthenticatedUser authenticatedUser, Credentials credentials) - throws GuacamoleException { - - // No update necessary - return context; - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java deleted file mode 100644 index f838063..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java +++ /dev/null @@ -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.oauth; - -import com.google.inject.AbstractModule; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.auth.oauth.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 OAuth-specific injections. - */ -public class OAuthAuthenticationProviderModule extends AbstractModule { - - /** - * Guacamole server environment. - */ - private final Environment environment; - - /** - * A reference to the OAuthAuthenticationProvider on behalf of which this - * module has configured injection. - */ - private final AuthenticationProvider authProvider; - - /** - * Creates a new OAuth authentication provider module which configures - * injection for the OAuthAuthenticationProvider. - * - * @param authProvider - * The AuthenticationProvider for which injection is being configured. - * - * @throws GuacamoleException - * If an error occurs while retrieving the Guacamole server - * environment. - */ - public OAuthAuthenticationProviderModule(AuthenticationProvider authProvider) - throws GuacamoleException { - - // Get local environment - this.environment = new LocalEnvironment(); - - // 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 OAuth-specific services - bind(ConfigurationService.class); - bind(TokenValidationService.class); - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java deleted file mode 100644 index 1304d58..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java +++ /dev/null @@ -1,139 +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.oauth.conf; - -import com.google.inject.Inject; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.environment.Environment; - -/** - * Service for retrieving configuration information regarding the OAuth service. - */ -public class ConfigurationService { - - /** - * The Guacamole server environment. - */ - @Inject - private Environment environment; - - /** - * Returns the authorization endpoint (URI) of the OAuth service as - * configured with guacamole.properties. - * - * @return - * The authorization endpoint of the OAuth service, as configured with - * guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the authorization - * endpoint property is missing. - */ - public String getAuthorizationEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_AUTHORIZATION_ENDPOINT); - } - - /** - * Returns the OAuth client ID which should be submitted to the OAuth - * service when necessary, as configured with guacamole.properties. This - * value is typically provided by the OAuth service when OAuth credentials - * are generated for your application. - * - * @return - * The client ID to use when communicating with the OAuth service, - * as configured with guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the client ID - * property is missing. - */ - public String getClientID() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_CLIENT_ID); - } - - /** - * Returns the URI that the OAuth service should redirect to after - * the authentication process is complete, as configured with - * guacamole.properties. This must be the full URL that a user would enter - * into their browser to access Guacamole. - * - * @return - * The client secret to use when communicating with the OAuth service, - * as configured with guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the redirect URI - * property is missing. - */ - public String getRedirectURI() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_REDIRECT_URI); - } - - /** - * Returns the issuer to expect for all received ID tokens, as configured - * with guacamole.properties. - * - * @return - * The issuer to expect for all received ID tokens, as configured with - * guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the issuer property - * is missing. - */ - public String getIssuer() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_ISSUER); - } - - /** - * Returns the endpoint (URI) of the JWKS service which defines how - * received ID tokens (JWTs) shall be validated, as configured with - * guacamole.properties. - * - * @return - * The endpoint (URI) of the JWKS service which defines how received ID - * tokens (JWTs) shall be validated, as configured with - * guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the JWKS endpoint - * property is missing. - */ - public String getJWKSEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_JWKS_ENDPOINT); - } - - /** - * Returns the claim type which contains the authenticated user's username - * within any valid JWT, as configured with guacamole.properties. - * - * @return - * The claim type which contains the authenticated user's username - * within any valid JWT, as configured with guacamole.properties. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the username claim - * type property is missing. - */ - public String getUsernameClaimType() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_USERNAME_CLAIM_TYPE); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java deleted file mode 100644 index cfb4eb3..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java +++ /dev/null @@ -1,108 +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.oauth.conf; - -import org.apache.guacamole.properties.StringGuacamoleProperty; - -/** - * Provides properties required for use of the OAuth authentication provider. - * These properties will be read from guacamole.properties when the OAuth - * authentication provider is used. - */ -public class OAuthGuacamoleProperties { - - /** - * This class should not be instantiated. - */ - private OAuthGuacamoleProperties() {} - - /** - * The authorization endpoint (URI) of the OAuth service. - */ - public static final StringGuacamoleProperty OAUTH_AUTHORIZATION_ENDPOINT = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-authorization-endpoint"; } - - }; - - /** - * The endpoint (URI) of the JWKS service which defines how received ID - * tokens (JWTs) shall be validated. - */ - public static final StringGuacamoleProperty OAUTH_JWKS_ENDPOINT = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-jwks-endpoint"; } - - }; - - /** - * The issuer to expect for all received ID tokens. - */ - public static final StringGuacamoleProperty OAUTH_ISSUER = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-issuer"; } - - }; - - /** - * The claim type which contains the authenticated user's username within - * any valid JWT. - */ - public static final StringGuacamoleProperty OAUTH_USERNAME_CLAIM_TYPE = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-username-claim-type"; } - - }; - - /** - * OAuth client ID which should be submitted to the OAuth service when - * necessary. This value is typically provided by the OAuth service when - * OAuth credentials are generated for your application. - */ - public static final StringGuacamoleProperty OAUTH_CLIENT_ID = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-client-id"; } - - }; - - /** - * The URI that the OAuth service should redirect to after the - * authentication process is complete. This must be the full URL that a - * user would enter into their browser to access Guacamole. - */ - public static final StringGuacamoleProperty OAUTH_REDIRECT_URI = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-redirect-uri"; } - - }; - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java deleted file mode 100644 index 5d6599f..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java +++ /dev/null @@ -1,118 +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.oauth.form; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.net.URLEncoder; -import java.security.SecureRandom; -import org.apache.guacamole.form.Field; - -/** - * Field definition which represents the token returned by an OAuth service. - * Within the user interface, this will be rendered as an appropriate "Log in - * with ..." button which links to the OAuth service. - */ -public class OAuthTokenField extends Field { - - /** - * The standard HTTP parameter which will be included within the URL by all - * OAuth services upon successful authentication and redirect. - */ - public static final String PARAMETER_NAME = "id_token"; - - /** - * The full URI which the field should link to. - */ - private final String authorizationURI; - - /** - * Cryptographically-secure random number generator for generating the - * required nonce. - */ - private static final SecureRandom random = new SecureRandom(); - - /** - * Generates a cryptographically-secure nonce value. The nonce is intended - * to be used to prevent replay attacks. - * - * @return - * A cryptographically-secure nonce value. - */ - private static String generateNonce() { - return new BigInteger(130, random).toString(32); - } - - /** - * Creates a new OAuth "id_token" field which links to the given OAuth - * service using the provided client ID. Successful authentication at the - * OAuth service will result in the client being redirected to the specified - * redirect URI. The OAuth 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 OAuth authentication - * requests. - * - * @param clientID - * The ID of the OAuth client. This is normally determined ahead of - * time by the OAuth service through some manual credential request - * procedure. - * - * @param redirectURI - * The URI that the OAuth service should redirect to upon successful - * authentication. - */ - public OAuthTokenField(String authorizationEndpoint, String clientID, - String redirectURI) { - - // Init base field properties - super(PARAMETER_NAME, "GUAC_OAUTH_TOKEN"); - - // Build authorization URI from given values - try { - this.authorizationURI = authorizationEndpoint - + "?scope=openid%20email%20profile" - + "&response_type=id_token" - + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") - + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") - + "&nonce=" + generateNonce(); - } - - // Java is required to provide UTF-8 support - catch (UnsupportedEncodingException e) { - throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e); - } - - } - - /** - * Returns the full URI that this field should link to when a new token - * needs to be obtained from the OAuth service. - * - * @return - * The full URI that this field should link to. - */ - public String getAuthorizationURI() { - return authorizationURI; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java deleted file mode 100644 index 84bfa3d..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java +++ /dev/null @@ -1,105 +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.oauth.token; - -import com.google.inject.Inject; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleSecurityException; -import org.apache.guacamole.GuacamoleServerException; -import org.jose4j.jwk.HttpsJwks; -import org.jose4j.jwt.JwtClaims; -import org.jose4j.jwt.MalformedClaimException; -import org.jose4j.jwt.consumer.InvalidJwtException; -import org.jose4j.jwt.consumer.JwtConsumer; -import org.jose4j.jwt.consumer.JwtConsumerBuilder; -import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver; - -/** - * Service for validating ID tokens forwarded to us by the client, verifying - * that they did indeed come from the OAuth service. - */ -public class TokenValidationService { - - /** - * Service for retrieving OAuth configuration information. - */ - @Inject - private ConfigurationService confService; - - /** - * Validates and parses the given ID token, returning the username contained - * therein, as defined by the username claim type given in - * guacamole.properties. If the username claim type is missing or the ID - * token is invalid, an exception is thrown instead. - * - * @param token - * The ID token to validate and parse. - * - * @return - * The username contained within the given ID token. - * - * @throws GuacamoleException - * If the ID token is not valid, the username claim type is missing, or - * guacamole.properties could not be parsed. - */ - public String processUsername(String token) throws GuacamoleException { - - // Validating the token requires a JWKS key resolver - HttpsJwks jwks = new HttpsJwks(confService.getJWKSEndpoint()); - HttpsJwksVerificationKeyResolver resolver = new HttpsJwksVerificationKeyResolver(jwks); - - // Create JWT consumer for validating received token - JwtConsumer jwtConsumer = new JwtConsumerBuilder() - .setRequireExpirationTime() - .setMaxFutureValidityInMinutes(300) - .setAllowedClockSkewInSeconds(30) - .setRequireSubject() - .setExpectedIssuer(confService.getIssuer()) - .setExpectedAudience(confService.getClientID()) - .setVerificationKeyResolver(resolver) - .build(); - - try { - - // Validate JWT - JwtClaims claims = jwtConsumer.processToClaims(token); - - // Pull username from claims - String username = claims.getStringClaimValue(confService.getUsernameClaimType()); - if (username == null) - throw new GuacamoleSecurityException("Username missing from token"); - - // Username successfully retrieved from the JWT - return username; - - } - - // Rethrow any failures to validate/parse the JWT - catch (InvalidJwtException e) { - throw new GuacamoleSecurityException("Invalid ID token.", e); - } - catch (MalformedClaimException e) { - throw new GuacamoleServerException("Unable to parse JWT claims.", e); - } - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java deleted file mode 100644 index 3a798eb..0000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java +++ /dev/null @@ -1,71 +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.oauth.user; - -import com.google.inject.Inject; -import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; -import org.apache.guacamole.net.auth.AuthenticationProvider; -import org.apache.guacamole.net.auth.Credentials; - -/** - * An OAuth-specific implementation of AuthenticatedUser, associating a - * username and particular set of credentials with the OAuth 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; - - /** - * 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. - */ - public void init(String username, Credentials credentials) { - this.credentials = credentials; - setIdentifier(username); - } - - @Override - public AuthenticationProvider getAuthenticationProvider() { - return authProvider; - } - - @Override - public Credentials getCredentials() { - return credentials; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java new file mode 100644 index 0000000..10dea3d --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.openid; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; +import org.apache.guacamole.auth.openid.conf.ConfigurationService; +import org.apache.guacamole.auth.openid.form.TokenField; +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.form.Field; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.credentials.CredentialsInfo; +import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Service providing convenience functions for the OpenID AuthenticationProvider + * implementation. + */ +public class AuthenticationProviderService { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); + + /** + * Service for retrieving OpenID configuration information. + */ + @Inject + private ConfigurationService confService; + + /** + * Service for validating received ID tokens. + */ + @Inject + private TokenValidationService tokenService; + + /** + * Provider for AuthenticatedUser objects. + */ + @Inject + private Provider<AuthenticatedUser> authenticatedUserProvider; + + /** + * Returns an AuthenticatedUser representing the user authenticated by the + * given credentials. + * + * @param credentials + * The credentials to use for authentication. + * + * @return + * An AuthenticatedUser representing the user authenticated by the + * given credentials. + * + * @throws GuacamoleException + * If an error occurs while authenticating the user, or if access is + * denied. + */ + public AuthenticatedUser authenticateUser(Credentials credentials) + throws GuacamoleException { + + String token = null; + + // Pull OpenID token from request if present + HttpServletRequest request = credentials.getRequest(); + if (request != null) + token = request.getParameter(TokenField.PARAMETER_NAME); + + // If token provided, validate and produce authenticated user + if (token != null) { + + // Create corresponding authenticated user + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init(tokenService.processUsername(token), credentials); + return authenticatedUser; + + } + + // Request OpenID token + throw new GuacamoleInvalidCredentialsException("Invalid login.", + new CredentialsInfo(Arrays.asList(new Field[] { + + // OpenID-specific token (will automatically redirect the user + // to the authorization page via JavaScript) + new TokenField( + confService.getAuthorizationEndpoint(), + confService.getClientID(), + confService.getRedirectURI() + ) + + })) + ); + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java new file mode 100644 index 0000000..7fa8548 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java @@ -0,0 +1,105 @@ +/* + * 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.Guice; +import com.google.inject.Injector; +import org.apache.guacamole.GuacamoleException; +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.UserContext; + +/** + * Guacamole authentication backend which authenticates users using an + * arbitrary external system implementing OpenID. No storage for connections is + * provided - only authentication. Storage must be provided by some other + * extension. + */ +public class OpenIDAuthenticationProvider implements AuthenticationProvider { + + /** + * Injector which will manage the object graph of this authentication + * provider. + */ + private final Injector injector; + + /** + * Creates a new OpenIDAuthenticationProvider that authenticates users + * against an OpenID service. + * + * @throws GuacamoleException + * If a required property is missing, or an error occurs while parsing + * a property. + */ + public OpenIDAuthenticationProvider() throws GuacamoleException { + + // Set up Guice injector. + injector = Guice.createInjector( + new OpenIDAuthenticationProviderModule(this) + ); + + } + + @Override + public String getIdentifier() { + 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); + + } + + @Override + public AuthenticatedUser updateAuthenticatedUser( + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + + // No update necessary + return authenticatedUser; + + } + + @Override + public UserContext getUserContext(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // No associated data whatsoever + return null; + + } + + @Override + public UserContext updateUserContext(UserContext context, + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { + + // No update necessary + return context; + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java new file mode 100644 index 0000000..9abd666 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.openid; + +import com.google.inject.AbstractModule; +import org.apache.guacamole.auth.openid.conf.ConfigurationService; +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 = new LocalEnvironment(); + + // 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(TokenValidationService.class); + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java new file mode 100644 index 0000000..650cf47 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java @@ -0,0 +1,140 @@ +/* + * 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.conf; + +import com.google.inject.Inject; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; + +/** + * Service for retrieving configuration information regarding the OpenID + * service. + */ +public class ConfigurationService { + + /** + * The Guacamole server environment. + */ + @Inject + private Environment environment; + + /** + * Returns the authorization endpoint (URI) of the OpenID service as + * configured with guacamole.properties. + * + * @return + * The authorization endpoint of the OpenID service, as configured with + * guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the authorization + * endpoint property is missing. + */ + public String getAuthorizationEndpoint() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_AUTHORIZATION_ENDPOINT); + } + + /** + * Returns the OpenID client ID which should be submitted to the OpenID + * service when necessary, as configured with guacamole.properties. This + * value is typically provided by the OpenID service when OpenID credentials + * are generated for your application. + * + * @return + * The client ID to use when communicating with the OpenID service, + * as configured with guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the client ID + * property is missing. + */ + public String getClientID() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_CLIENT_ID); + } + + /** + * Returns the URI that the OpenID service should redirect to after + * the authentication process is complete, as configured with + * guacamole.properties. This must be the full URL that a user would enter + * into their browser to access Guacamole. + * + * @return + * The client secret to use when communicating with the OpenID service, + * as configured with guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the redirect URI + * property is missing. + */ + public String getRedirectURI() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_REDIRECT_URI); + } + + /** + * Returns the issuer to expect for all received ID tokens, as configured + * with guacamole.properties. + * + * @return + * The issuer to expect for all received ID tokens, as configured with + * guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the issuer property + * is missing. + */ + public String getIssuer() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_ISSUER); + } + + /** + * Returns the endpoint (URI) of the JWKS service which defines how + * received ID tokens (JWTs) shall be validated, as configured with + * guacamole.properties. + * + * @return + * The endpoint (URI) of the JWKS service which defines how received ID + * tokens (JWTs) shall be validated, as configured with + * guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the JWKS endpoint + * property is missing. + */ + public String getJWKSEndpoint() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_JWKS_ENDPOINT); + } + + /** + * Returns the claim type which contains the authenticated user's username + * within any valid JWT, as configured with guacamole.properties. + * + * @return + * The claim type which contains the authenticated user's username + * within any valid JWT, as configured with guacamole.properties. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed, or if the username claim + * type property is missing. + */ + public String getUsernameClaimType() throws GuacamoleException { + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_USERNAME_CLAIM_TYPE); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.java new file mode 100644 index 0000000..2049cca --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.java @@ -0,0 +1,108 @@ +/* + * 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.conf; + +import org.apache.guacamole.properties.StringGuacamoleProperty; + +/** + * Provides properties required for use of the OpenID authentication provider. + * These properties will be read from guacamole.properties when the OpenID + * authentication provider is used. + */ +public class OpenIDGuacamoleProperties { + + /** + * This class should not be instantiated. + */ + private OpenIDGuacamoleProperties() {} + + /** + * The authorization endpoint (URI) of the OpenID service. + */ + public static final StringGuacamoleProperty OPENID_AUTHORIZATION_ENDPOINT = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-authorization-endpoint"; } + + }; + + /** + * The endpoint (URI) of the JWKS service which defines how received ID + * tokens (JWTs) shall be validated. + */ + public static final StringGuacamoleProperty OPENID_JWKS_ENDPOINT = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-jwks-endpoint"; } + + }; + + /** + * The issuer to expect for all received ID tokens. + */ + public static final StringGuacamoleProperty OPENID_ISSUER = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-issuer"; } + + }; + + /** + * The claim type which contains the authenticated user's username within + * any valid JWT. + */ + public static final StringGuacamoleProperty OPENID_USERNAME_CLAIM_TYPE = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-username-claim-type"; } + + }; + + /** + * OpenID client ID which should be submitted to the OpenID service when + * necessary. This value is typically provided by the OpenID service when + * OpenID credentials are generated for your application. + */ + public static final StringGuacamoleProperty OPENID_CLIENT_ID = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-client-id"; } + + }; + + /** + * The URI that the OpenID service should redirect to after the + * authentication process is complete. This must be the full URL that a + * user would enter into their browser to access Guacamole. + */ + public static final StringGuacamoleProperty OPENID_REDIRECT_URI = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-redirect-uri"; } + + }; + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java new file mode 100644 index 0000000..3ef5d94 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java @@ -0,0 +1,118 @@ +/* + * 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.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.net.URLEncoder; +import java.security.SecureRandom; +import org.apache.guacamole.form.Field; + +/** + * Field definition which represents the token returned by an OpenID service. + * Within the user interface, this will be rendered as an appropriate "Log in + * with ..." button which links to the OpenID service. + */ +public class TokenField extends Field { + + /** + * 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"; + + /** + * The full URI which the field should link to. + */ + private final String authorizationURI; + + /** + * Cryptographically-secure random number generator for generating the + * required nonce. + */ + private static final SecureRandom random = new SecureRandom(); + + /** + * Generates a cryptographically-secure nonce value. The nonce is intended + * to be used to prevent replay attacks. + * + * @return + * A cryptographically-secure nonce value. + */ + private static String generateNonce() { + return new BigInteger(130, random).toString(32); + } + + /** + * Creates a new OpenID "id_token" field which links to the given OpenID + * service using the provided client ID. Successful authentication at the + * OpenID 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 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. + */ + public TokenField(String authorizationEndpoint, String clientID, + String redirectURI) { + + // Init base field properties + super(PARAMETER_NAME, "GUAC_OPENID_TOKEN"); + + // Build authorization URI from given values + try { + this.authorizationURI = authorizationEndpoint + + "?scope=openid%20email%20profile" + + "&response_type=id_token" + + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") + + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") + + "&nonce=" + generateNonce(); + } + + // Java is required to provide UTF-8 support + catch (UnsupportedEncodingException e) { + throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e); + } + + } + + /** + * Returns the full URI that this field should link to when a new token + * needs to be obtained from the OpenID service. + * + * @return + * The full URI that this field should link to. + */ + public String getAuthorizationURI() { + return authorizationURI; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java new file mode 100644 index 0000000..b1a8a28 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java @@ -0,0 +1,105 @@ +/* + * 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.token; + +import com.google.inject.Inject; +import org.apache.guacamole.auth.openid.conf.ConfigurationService; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.GuacamoleServerException; +import org.jose4j.jwk.HttpsJwks; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.MalformedClaimException; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver; + +/** + * Service for validating ID tokens forwarded to us by the client, verifying + * that they did indeed come from the OpenID service. + */ +public class TokenValidationService { + + /** + * Service for retrieving OpenID configuration information. + */ + @Inject + private ConfigurationService confService; + + /** + * Validates and parses the given ID token, returning the username contained + * therein, as defined by the username claim type given in + * guacamole.properties. If the username claim type is missing or the ID + * token is invalid, an exception is thrown instead. + * + * @param token + * The ID token to validate and parse. + * + * @return + * The username contained within the given ID token. + * + * @throws GuacamoleException + * If the ID token is not valid, the username claim type is missing, or + * guacamole.properties could not be parsed. + */ + public String processUsername(String token) throws GuacamoleException { + + // Validating the token requires a JWKS key resolver + HttpsJwks jwks = new HttpsJwks(confService.getJWKSEndpoint()); + HttpsJwksVerificationKeyResolver resolver = new HttpsJwksVerificationKeyResolver(jwks); + + // Create JWT consumer for validating received token + JwtConsumer jwtConsumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setMaxFutureValidityInMinutes(300) + .setAllowedClockSkewInSeconds(30) + .setRequireSubject() + .setExpectedIssuer(confService.getIssuer()) + .setExpectedAudience(confService.getClientID()) + .setVerificationKeyResolver(resolver) + .build(); + + try { + + // Validate JWT + JwtClaims claims = jwtConsumer.processToClaims(token); + + // Pull username from claims + String username = claims.getStringClaimValue(confService.getUsernameClaimType()); + if (username == null) + throw new GuacamoleSecurityException("Username missing from token"); + + // Username successfully retrieved from the JWT + return username; + + } + + // Rethrow any failures to validate/parse the JWT + catch (InvalidJwtException e) { + throw new GuacamoleSecurityException("Invalid ID token.", e); + } + catch (MalformedClaimException e) { + throw new GuacamoleServerException("Unable to parse JWT claims.", e); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/user/AuthenticatedUser.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/user/AuthenticatedUser.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/user/AuthenticatedUser.java new file mode 100644 index 0000000..b7ff125 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/user/AuthenticatedUser.java @@ -0,0 +1,71 @@ +/* + * 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 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 and particular set of credentials 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; + + /** + * 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. + */ + public void init(String username, Credentials credentials) { + this.credentials = credentials; + setIdentifier(username); + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return authProvider; + } + + @Override + public Credentials getCredentials() { + return credentials; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json index add9607..3707a4f 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -2,17 +2,17 @@ "guacamoleVersion" : "0.9.11-incubating", - "name" : "OAuth Authentication Extension", - "namespace" : "guac-oauth", + "name" : "OpenID Authentication Extension", + "namespace" : "guac-openid", "authProviders" : [ - "org.apache.guacamole.auth.oauth.OAuthAuthenticationProvider" + "org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider" ], "js" : [ - "oauthModule.js", - "oauthController.js", - "oauthConfig.js" + "openidModule.js", + "openidController.js", + "openidConfig.js" ] } http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js b/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js deleted file mode 100644 index 4319656..0000000 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js +++ /dev/null @@ -1,54 +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. - */ - -/** - * Config block which registers OAuth-specific field types. - */ -angular.module('guacOAuth').config(['formServiceProvider', - function guacOAuthConfig(formServiceProvider) { - - // Define field for token from OAuth service - formServiceProvider.registerFieldType("GUAC_OAUTH_TOKEN", { - template : '', - controller : 'guacOAuthController', - module : 'guacOAuth' - }); - -}]); - -/** - * Config block which augments the existing routing, providing special handling - * for the "id_token=" fragments provided by OpenID Connect. - */ -angular.module('index').config(['$routeProvider', - function indexRouteConfig($routeProvider) { - - // Transform "/#/id_token=..." to "/#/?id_token=..." - $routeProvider.when('/id_token=:response', { - - template : '', - controller : ['$location', function reroute($location) { - var params = $location.path().substring(1); - $location.url('/'); - $location.search(params); - }] - - }); - -}]); http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/oauthController.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthController.js b/extensions/guacamole-auth-openid/src/main/resources/oauthController.js deleted file mode 100644 index ba7a120..0000000 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthController.js +++ /dev/null @@ -1,30 +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. - */ - -/** - * Controller for the "GUAC_OAUTH_TOKEN" field which simply redirects the user - * immediately to the authorization URI. - */ -angular.module('guacOAuth').controller('guacOAuthController', ['$scope', - function guacOAuthController($scope) { - - // Redirect to authorization URI - window.location = $scope.field.authorizationURI; - -}]); http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js b/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js deleted file mode 100644 index 545b6b7..0000000 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js +++ /dev/null @@ -1,28 +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. - */ - -/** - * Module which provides handling for OAuth authentication. - */ -angular.module('guacOAuth', [ - 'form' -]); - -// Ensure the OAuth module is loaded along with the rest of the app -angular.module('index').requires.push('guacOAuth'); http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js new file mode 100644 index 0000000..455c66a --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Config block which registers openid-specific field types. + */ +angular.module('guacOpenID').config(['formServiceProvider', + function guacOpenIDConfig(formServiceProvider) { + + // Define field for token from OpenID service + formServiceProvider.registerFieldType("GUAC_OPENID_TOKEN", { + template : '', + controller : 'guacOpenIDController', + module : 'guacOpenID' + }); + +}]); + +/** + * Config block which augments the existing routing, providing special handling + * for the "id_token=" fragments provided by OpenID Connect. + */ +angular.module('index').config(['$routeProvider', + function indexRouteConfig($routeProvider) { + + // Transform "/#/id_token=..." to "/#/?id_token=..." + $routeProvider.when('/id_token=:response', { + + template : '', + controller : ['$location', function reroute($location) { + var params = $location.path().substring(1); + $location.url('/'); + $location.search(params); + }] + + }); + +}]); http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/openidController.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidController.js b/extensions/guacamole-auth-openid/src/main/resources/openidController.js new file mode 100644 index 0000000..a1fad88 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/openidController.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * Controller for the "GUAC_OPENID_TOKEN" field which simply redirects the user + * immediately to the authorization URI. + */ +angular.module('guacOpenID').controller('guacOpenIDController', ['$scope', + function guacOpenIDController($scope) { + + // Redirect to authorization URI + window.location = $scope.field.authorizationURI; + +}]); http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d04d6122/extensions/guacamole-auth-openid/src/main/resources/openidModule.js ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidModule.js b/extensions/guacamole-auth-openid/src/main/resources/openidModule.js new file mode 100644 index 0000000..e8fce23 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/openidModule.js @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/** + * Module which provides handling for OpenID authentication. + */ +angular.module('guacOpenID', [ + 'form' +]); + +// Ensure the OpenID module is loaded along with the rest of the app +angular.module('index').requires.push('guacOpenID');
