From 67c817af9ff1da7729a03205b929119f6d020027 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 1 Jan 2016 19:54:58 -0800 Subject: [PATCH 01/28] GUACAMOLE-210: Add stub OAuthAuthenticationProvider. --- extensions/guacamole-auth-openid/pom.xml | 84 +++++++++++++++++++ .../oauth/OAuthAuthenticationProvider.java | 83 ++++++++++++++++++ .../src/main/resources/guac-manifest.json | 12 +++ pom.xml | 1 + 4 files changed, 180 insertions(+) create mode 100644 extensions/guacamole-auth-openid/pom.xml create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java create mode 100644 extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml new file mode 100644 index 000000000..bc62695b8 --- /dev/null +++ b/extensions/guacamole-auth-openid/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + org.apache.guacamole + guacamole-auth-openid + jar + 0.9.9 + guacamole-auth-openid + http://guacamole.incubator.apache.org/ + + + UTF-8 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + + + + + + + org.glyptodon.guacamole + guacamole-ext + 0.9.9 + provided + + + + + com.google.oauth-client + google-oauth-client + 1.21.0 + + + + + com.google.inject + guice + 3.0 + + + com.google.inject.extensions + guice-multibindings + 3.0 + + + + + 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 new file mode 100644 index 000000000..55f40b1b9 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java @@ -0,0 +1,83 @@ +/* + * 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 org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; +import org.glyptodon.guacamole.net.auth.AuthenticationProvider; +import org.glyptodon.guacamole.net.auth.Credentials; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; +import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; + +/** + * 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 { + + @Override + public String getIdentifier() { + return "oauth"; + } + + @Override + public AuthenticatedUser authenticateUser(Credentials credentials) + throws GuacamoleException { + + // STUB + throw new GuacamoleInvalidCredentialsException( + "Invalid login.", + CredentialsInfo.USERNAME_PASSWORD + ); + + } + + @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) throws GuacamoleException { + + // No update necessary + return context; + + } + +} diff --git a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..77dd70922 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -0,0 +1,12 @@ +{ + + "guacamoleVersion" : "0.9.9", + + "name" : "OAuth Authentication Extension", + "namespace" : "guac-oauth", + + "authProviders" : [ + "org.apache.guacamole.auth.oauth.OAuthAuthenticationProvider" + ] + +} diff --git a/pom.xml b/pom.xml index 41317a316..55228f5aa 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ extensions/guacamole-auth-jdbc extensions/guacamole-auth-ldap extensions/guacamole-auth-noauth + extensions/guacamole-auth-openid doc/guacamole-example From c7d5bd69aa33e8d28c5391176ae7977574c660f7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 1 Jan 2016 23:54:09 -0800 Subject: [PATCH 02/28] GUACAMOLE-210: Add OAuth code field generated from configuration info in guacamole.properties. Use Guice. --- .../oauth/AuthenticationProviderService.java | 89 +++++++++++++++ .../auth/oauth/ConfigurationService.java | 107 ++++++++++++++++++ .../oauth/OAuthAuthenticationProvider.java | 35 ++++-- .../OAuthAuthenticationProviderModule.java | 78 +++++++++++++ .../guacamole/auth/oauth/OAuthCodeField.java | 97 ++++++++++++++++ .../auth/oauth/OAuthGuacamoleProperties.java | 86 ++++++++++++++ 6 files changed, 485 insertions(+), 7 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.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 new file mode 100644 index 000000000..c07a78cd4 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java @@ -0,0 +1,89 @@ +/* + * 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 java.util.Arrays; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.form.Field; +import org.glyptodon.guacamole.net.auth.AuthenticatedUser; +import org.glyptodon.guacamole.net.auth.Credentials; +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; +import org.glyptodon.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; + + /** + * 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 { + + // Request auth code + throw new GuacamoleInvalidCredentialsException("Invalid login.", + new CredentialsInfo(Arrays.asList(new Field[] { + + // Normal username/password fields + CredentialsInfo.USERNAME, + CredentialsInfo.PASSWORD, + + // OAuth-specific code (will be rendered as an appropriate + // "Log in with..." button + new OAuthCodeField( + confService.getAuthorizationEndpoint(), + confService.getClientID(), + confService.getRedirectURI() + ) + + })) + ); + + } + +} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java new file mode 100644 index 000000000..d27b0752b --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java @@ -0,0 +1,107 @@ +/* + * 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 org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.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 OAuth client secret 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 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 client secret + * property is missing. + */ + public String getClientSecret() throws GuacamoleException { + return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_CLIENT_SECRET); + } + + /** + * 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 client secret + * property is missing. + */ + public String getRedirectURI() throws GuacamoleException { + return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_REDIRECT_URI); + } + +} 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 index 55f40b1b9..06255ac80 100644 --- 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 @@ -19,13 +19,13 @@ package org.apache.guacamole.auth.oauth; +import com.google.inject.Guice; +import com.google.inject.Injector; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; -import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; /** * Guacamole authentication backend which authenticates users using an @@ -35,6 +35,29 @@ import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsE */ 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"; @@ -44,11 +67,9 @@ public class OAuthAuthenticationProvider implements AuthenticationProvider { public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { - // STUB - throw new GuacamoleInvalidCredentialsException( - "Invalid login.", - CredentialsInfo.USERNAME_PASSWORD - ); + // Attempt to authenticate user with given credentials + AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class); + return authProviderService.authenticateUser(credentials); } 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 new file mode 100644 index 000000000..66860d1ab --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java @@ -0,0 +1,78 @@ +/* + * 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.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.environment.Environment; +import org.glyptodon.guacamole.environment.LocalEnvironment; +import org.glyptodon.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); + + } + +} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java new file mode 100644 index 000000000..6f4e4689c --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java @@ -0,0 +1,97 @@ +/* + * 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 java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import org.glyptodon.guacamole.form.Field; + +/** + * Field definition which represents the code 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 OAuthCodeField extends Field { + + /** + * The standard HTTP parameter which will be included within the URL by all + * OAuth services upon successful authentication and redirect. + */ + private static final String OAUTH_CODE_PARAMETER_NAME = "code"; + + /** + * The full URI which the field should link to. + */ + private final String authorizationURI; + + /** + * Creates a new OAuth "code" 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 code will be embedded in the query parameters of + * that URI. + * + * @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 OAuthCodeField(String authorizationEndpoint, String clientID, + String redirectURI) { + + // Init base field properties + super(OAUTH_CODE_PARAMETER_NAME, "OAUTH_CODE"); + + // Build authorization URI from given values + try { + this.authorizationURI = authorizationEndpoint + + "?scope=openid%20email%20profile" + + "&response_type=code" + + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") + + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8"); + } + + // 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 code + * needs to be obtained from the OAuth service. + * + * @return + * The full URI that this field should link to. + */ + public String getAuthorizationURI() { + return authorizationURI; + } + +} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java new file mode 100644 index 000000000..bb1e4fddc --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java @@ -0,0 +1,86 @@ +/* + * 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 org.glyptodon.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"; } + + }; + + /** + * 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"; } + + }; + + /** + * OAuth client secret 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_SECRET = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "oauth-client-secret"; } + + }; + + /** + * 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"; } + + }; + +} From 89f25a9467b62545bd1c114a35bba8680678fcc6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 00:22:08 -0800 Subject: [PATCH 03/28] GUACAMOLE-210: Add OAuth code/link field. --- .../guacamole/auth/oauth/OAuthCodeField.java | 2 +- .../src/main/resources/guac-manifest.json | 11 ++++++- .../src/main/resources/oauthCodeField.html | 1 + .../src/main/resources/oauthConfig.js | 31 +++++++++++++++++++ .../src/main/resources/oauthModule.js | 28 +++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html create mode 100644 extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js create mode 100644 extensions/guacamole-auth-openid/src/main/resources/oauthModule.js diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java index 6f4e4689c..bdf16c850 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java @@ -65,7 +65,7 @@ public class OAuthCodeField extends Field { String redirectURI) { // Init base field properties - super(OAUTH_CODE_PARAMETER_NAME, "OAUTH_CODE"); + super(OAUTH_CODE_PARAMETER_NAME, "GUAC_OAUTH_CODE"); // Build authorization URI from given values try { 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 77dd70922..e8f2facc1 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -7,6 +7,15 @@ "authProviders" : [ "org.apache.guacamole.auth.oauth.OAuthAuthenticationProvider" - ] + ], + + "js" : [ + "oauthModule.js", + "oauthConfig.js" + ], + + "resources" : { + "oauthCodeField.html" : "text/html" + } } diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html b/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html new file mode 100644 index 000000000..e6c4fff04 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html @@ -0,0 +1 @@ +Log in using OAuth \ No newline at end of file diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js b/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js new file mode 100644 index 000000000..ba6f0cc88 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js @@ -0,0 +1,31 @@ +/* + * 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 code from OAuth service + formServiceProvider.registerFieldType("GUAC_OAUTH_CODE", { + templateUrl : 'app/ext/guac-oauth/oauthCodeField.html' + }); + +}]); diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js b/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js new file mode 100644 index 000000000..545b6b79c --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/oauthModule.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 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'); From 77e714b0e15dcbdaa5a0afc261e9a3592a8ee494 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 00:36:12 -0800 Subject: [PATCH 04/28] GUACAMOLE-210: Stub out authentication (recognize but do not actually use code). --- extensions/guacamole-auth-openid/pom.xml | 8 +++ .../oauth/AuthenticationProviderService.java | 24 ++++++- .../guacamole/auth/oauth/OAuthCodeField.java | 4 +- .../auth/oauth/user/AuthenticatedUser.java | 71 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index bc62695b8..9ec561c81 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -79,6 +79,14 @@ 3.0 + + + javax.servlet + servlet-api + 2.5 + provided + + 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 index c07a78cd4..a183889b3 100644 --- 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 @@ -20,10 +20,12 @@ 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.user.AuthenticatedUser; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.form.Field; -import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; @@ -47,6 +49,12 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; + /** + * Provider for AuthenticatedUser objects. + */ + @Inject + private Provider authenticatedUserProvider; + /** * Returns an AuthenticatedUser representing the user authenticated by the * given credentials. @@ -65,6 +73,20 @@ public class AuthenticationProviderService { public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { + String code = null; + + // Pull OAuth code from request if present + HttpServletRequest request = credentials.getRequest(); + if (request != null) + code = request.getParameter(OAuthCodeField.PARAMETER_NAME); + + // TODO: Actually complete authentication using received code + if (code != null) { + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init("STUB", credentials); + return authenticatedUser; + } + // Request auth code throw new GuacamoleInvalidCredentialsException("Invalid login.", new CredentialsInfo(Arrays.asList(new Field[] { diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java index bdf16c850..35ae5ebef 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java @@ -34,7 +34,7 @@ public class OAuthCodeField extends Field { * The standard HTTP parameter which will be included within the URL by all * OAuth services upon successful authentication and redirect. */ - private static final String OAUTH_CODE_PARAMETER_NAME = "code"; + public static final String PARAMETER_NAME = "code"; /** * The full URI which the field should link to. @@ -65,7 +65,7 @@ public class OAuthCodeField extends Field { String redirectURI) { // Init base field properties - super(OAUTH_CODE_PARAMETER_NAME, "GUAC_OAUTH_CODE"); + super(PARAMETER_NAME, "GUAC_OAUTH_CODE"); // Build authorization URI from given values try { 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 new file mode 100644 index 000000000..935c27070 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/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.oauth.user; + +import com.google.inject.Inject; +import org.glyptodon.guacamole.net.auth.AbstractAuthenticatedUser; +import org.glyptodon.guacamole.net.auth.AuthenticationProvider; +import org.glyptodon.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; + } + +} From 1c6a603a08c9e291205c10e2a38a2e4e53533e24 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 00:43:18 -0800 Subject: [PATCH 05/28] GUACAMOLE-210: Move classes to reasonable packages. --- .../guacamole/auth/oauth/AuthenticationProviderService.java | 2 ++ .../guacamole/auth/oauth/OAuthAuthenticationProviderModule.java | 1 + .../guacamole/auth/oauth/{ => conf}/ConfigurationService.java | 2 +- .../auth/oauth/{ => conf}/OAuthGuacamoleProperties.java | 2 +- .../apache/guacamole/auth/oauth/{ => form}/OAuthCodeField.java | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/{ => conf}/ConfigurationService.java (98%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/{ => conf}/OAuthGuacamoleProperties.java (98%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/{ => form}/OAuthCodeField.java (98%) 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 index a183889b3..a1b9c462d 100644 --- 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 @@ -24,6 +24,8 @@ import com.google.inject.Provider; import java.util.Arrays; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; +import org.apache.guacamole.auth.oauth.conf.ConfigurationService; +import org.apache.guacamole.auth.oauth.form.OAuthCodeField; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.form.Field; import org.glyptodon.guacamole.net.auth.Credentials; 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 index 66860d1ab..e31c9457f 100644 --- 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 @@ -20,6 +20,7 @@ package org.apache.guacamole.auth.oauth; import com.google.inject.AbstractModule; +import org.apache.guacamole.auth.oauth.conf.ConfigurationService; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java similarity index 98% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java index d27b0752b..f98721833 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/ConfigurationService.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +package org.apache.guacamole.auth.oauth.conf; import com.google.inject.Inject; import org.glyptodon.guacamole.GuacamoleException; diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java similarity index 98% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java index bb1e4fddc..2098d45b7 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthGuacamoleProperties.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +package org.apache.guacamole.auth.oauth.conf; import org.glyptodon.guacamole.properties.StringGuacamoleProperty; diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java similarity index 98% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java index 35ae5ebef..9b0764a5e 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthCodeField.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +package org.apache.guacamole.auth.oauth.form; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; From 63b69ad0762676f6f3608cbd889f28b6c94c6c09 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 21:10:30 -0800 Subject: [PATCH 06/28] GUACAMOLE-210: Use same version of Jersey as the main Guacamole webapp. --- extensions/guacamole-auth-openid/pom.xml | 39 +++++- .../guacamole/auth/oauth/TokenResponse.java | 113 ++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 9ec561c81..d443cddee 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -41,12 +41,38 @@ org.apache.maven.plugins maven-compiler-plugin + 3.3 1.6 1.6 + + -Xlint:all + -Werror + + true + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + prepare-package + + unpack-dependencies + + + runtime + ${project.build.directory}/classes + + + + + @@ -60,11 +86,16 @@ provided - + - com.google.oauth-client - google-oauth-client - 1.21.0 + com.sun.jersey + jersey-client + 1.17.1 + + + com.sun.jersey + jersey-json + 1.17.1 diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java new file mode 100644 index 000000000..329fb1626 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.oauth; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; +import org.glyptodon.guacamole.GuacamoleException; +import org.apache.guacamole.auth.oauth.conf.ConfigurationService; +import org.apache.guacamole.auth.oauth.form.OAuthCodeField; +import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; +import org.glyptodon.guacamole.form.Field; +import org.glyptodon.guacamole.net.auth.Credentials; +import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; +import org.glyptodon.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; + + /** + * Provider for AuthenticatedUser objects. + */ + @Inject + private Provider 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 code = null; + + // Pull OAuth code from request if present + HttpServletRequest request = credentials.getRequest(); + if (request != null) + code = request.getParameter(OAuthCodeField.PARAMETER_NAME); + + // TODO: Actually complete authentication using received code + if (code != null) { + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init("STUB", credentials); + return authenticatedUser; + } + + // Request auth code + throw new GuacamoleInvalidCredentialsException("Invalid login.", + new CredentialsInfo(Arrays.asList(new Field[] { + + // Normal username/password fields + CredentialsInfo.USERNAME, + CredentialsInfo.PASSWORD, + + // OAuth-specific code (will be rendered as an appropriate + // "Log in with..." button + new OAuthCodeField( + confService.getAuthorizationEndpoint(), + confService.getClientID(), + confService.getRedirectURI() + ) + + })) + ); + + } + +} From c20271cb9941854e46e85a5f975e4fa2aa832fea Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 21:55:53 -0800 Subject: [PATCH 07/28] GUACAMOLE-210: Add remaining endpoint properties. --- .../auth/oauth/conf/ConfigurationService.java | 16 ++++++++++++++++ .../oauth/conf/OAuthGuacamoleProperties.java | 11 +++++++++++ 2 files changed, 27 insertions(+) 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 index f98721833..e1567d239 100644 --- 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 @@ -50,6 +50,22 @@ public class ConfigurationService { return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_AUTHORIZATION_ENDPOINT); } + /** + * Returns the token endpoint (URI) of the OAuth service as configured with + * guacamole.properties. + * + * @return + * The token 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 getTokenEndpoint() throws GuacamoleException { + return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_TOKEN_ENDPOINT); + } + /** * Returns the OAuth client ID which should be submitted to the OAuth * service when necessary, as configured with guacamole.properties. This 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 index 2098d45b7..0ebb94f3a 100644 --- 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 @@ -44,6 +44,17 @@ public class OAuthGuacamoleProperties { }; + /** + * The token endpoint (URI) of the OAuth service. + */ + public static final StringGuacamoleProperty OAUTH_TOKEN_ENDPOINT = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "oauth-token-endpoint"; } + + }; + /** * OAuth client ID which should be submitted to the OAuth service when * necessary. This value is typically provided by the OAuth service when From c3c6e0c43b749929ae88d3c40d1da7bad2aa0309 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 2 Jan 2016 22:35:47 -0800 Subject: [PATCH 08/28] GUACAMOLE-210: POST code to OAuth service to retrieve token. --- .../oauth/AuthenticationProviderService.java | 15 ++ .../OAuthAuthenticationProviderModule.java | 25 +++ .../guacamole/auth/oauth/TokenResponse.java | 113 ------------- .../auth/oauth/token/TokenResponse.java | 153 ++++++++++++++++++ .../auth/oauth/token/TokenService.java | 101 ++++++++++++ 5 files changed, 294 insertions(+), 113 deletions(-) delete mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.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 index a1b9c462d..5783faa5d 100644 --- 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 @@ -26,6 +26,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; import org.apache.guacamole.auth.oauth.conf.ConfigurationService; import org.apache.guacamole.auth.oauth.form.OAuthCodeField; +import org.apache.guacamole.auth.oauth.token.TokenResponse; +import org.apache.guacamole.auth.oauth.token.TokenService; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.form.Field; import org.glyptodon.guacamole.net.auth.Credentials; @@ -51,6 +53,12 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; + /** + * Service for producing authentication tokens from OAuth codes. + */ + @Inject + private TokenService tokenService; + /** * Provider for AuthenticatedUser objects. */ @@ -84,9 +92,16 @@ public class AuthenticationProviderService { // TODO: Actually complete authentication using received code if (code != null) { + + // POST code and client information to OAuth token endpoint + TokenResponse response = tokenService.getTokenFromCode(code); + logger.debug("RESPONSE: {}", response); + + // Create corresponding authenticated user AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); authenticatedUser.init("STUB", credentials); return authenticatedUser; + } // Request auth code 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 index e31c9457f..a5cef6da5 100644 --- 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 @@ -20,7 +20,13 @@ package org.apache.guacamole.auth.oauth; import com.google.inject.AbstractModule; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; import org.apache.guacamole.auth.oauth.conf.ConfigurationService; +import org.apache.guacamole.auth.oauth.token.TokenService; +import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider; +import org.codehaus.jackson.map.DeserializationConfig; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.LocalEnvironment; @@ -42,6 +48,12 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { */ private final AuthenticationProvider authProvider; + /** + * A reference to the shared HTTP client to be used when making calls to + * the OAuth service. + */ + private final Client client; + /** * Creates a new OAuth authentication provider module which configures * injection for the OAuthAuthenticationProvider. @@ -62,6 +74,15 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { // Store associated auth provider this.authProvider = authProvider; + // Set up configuration for HTTP client + ClientConfig clientConfig = new DefaultClientConfig(); + clientConfig.getSingletons().add(new JacksonJaxbJsonProvider() + .configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) + ); + + // Store pre-configured HTTP client + this.client = Client.create(clientConfig); + } @Override @@ -73,6 +94,10 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { // Bind OAuth-specific services bind(ConfigurationService.class); + bind(TokenService.class); + + // Bind HTTP client + bind(Client.class).toInstance(client); } diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java deleted file mode 100644 index 329fb1626..000000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/TokenResponse.java +++ /dev/null @@ -1,113 +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.glyptodon.guacamole.GuacamoleException; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.auth.oauth.form.OAuthCodeField; -import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; -import org.glyptodon.guacamole.form.Field; -import org.glyptodon.guacamole.net.auth.Credentials; -import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; -import org.glyptodon.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; - - /** - * Provider for AuthenticatedUser objects. - */ - @Inject - private Provider 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 code = null; - - // Pull OAuth code from request if present - HttpServletRequest request = credentials.getRequest(); - if (request != null) - code = request.getParameter(OAuthCodeField.PARAMETER_NAME); - - // TODO: Actually complete authentication using received code - if (code != null) { - AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init("STUB", credentials); - return authenticatedUser; - } - - // Request auth code - throw new GuacamoleInvalidCredentialsException("Invalid login.", - new CredentialsInfo(Arrays.asList(new Field[] { - - // Normal username/password fields - CredentialsInfo.USERNAME, - CredentialsInfo.PASSWORD, - - // OAuth-specific code (will be rendered as an appropriate - // "Log in with..." button - new OAuthCodeField( - confService.getAuthorizationEndpoint(), - confService.getClientID(), - confService.getRedirectURI() - ) - - })) - ); - - } - -} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java new file mode 100644 index 000000000..513683041 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java @@ -0,0 +1,153 @@ +/* + * 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 org.codehaus.jackson.annotate.JsonProperty; + +/** + * The response produced from a successful request to the token endpoint of an + * OAuth service. + */ +public class TokenResponse { + + /** + * An arbitrary access token which can be used for future requests against + * the API associated with the OAuth service. + */ + private String accessToken; + + /** + * The type of token present. This will always be "Bearer". + */ + private String tokenType; + + /** + * The number of seconds the access token will remain valid. + */ + private int expiresIn; + + /** + * A JWT (JSON Web Token) which containing identity information which has + * been cryptographically signed. + */ + private String idToken; + + /** + * Returns an arbitrary access token which can be used for future requests + * against the API associated with the OAuth service. + * + * @return + * An arbitrary access token provided by the OAuth service. + */ + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + /** + * Sets the arbitrary access token which can be used for future requests + * against the API associated with the OAuth service. + * + * @param accessToken + * The arbitrary access token provided by the OAuth service. + */ + @JsonProperty("access_token") + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + /** + * Returns the type of token present in this response. This should always + * be "Bearer". + * + * @return + * The type of token present in this response. + */ + @JsonProperty("token_type") + public String getTokenType() { + return tokenType; + } + + /** + * Sets the type of token present in this response. This should always be + * "Bearer". + * + * @param tokenType + * The type of token present in this response, which should be + * "Bearer". + */ + @JsonProperty("token_type") + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + /** + * Returns the number of seconds the access token within this response will + * remain valid. + * + * @return + * The number of seconds the access token within this response will + * remain valid. + */ + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + /** + * Sets the number of seconds the access token within this response will + * remain valid. + * + * @param expiresIn + * The number of seconds the access token within this response will + * remain valid. + */ + @JsonProperty("expires_in") + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + /** + * Returns a JWT (JSON Web Token) containing identity information which has + * been cryptographically signed by the OAuth service. + * + * @return + * A JWT (JSON Web Token) containing identity information which has + * been cryptographically signed by the OAuth service. + */ + @JsonProperty("id_token") + public String getIdToken() { + return idToken; + } + + /** + * Sets the JWT (JSON Web Token) containing identity information which has + * been cryptographically signed by the OAuth service. + * + * @param idToken + * A JWT (JSON Web Token) containing identity information which has + * been cryptographically signed by the OAuth service. + */ + @JsonProperty("id_token") + public void setIdToken(String idToken) { + this.idToken = idToken; + } + +} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java new file mode 100644 index 000000000..a328bde4d --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java @@ -0,0 +1,101 @@ +/* + * 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 com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.representation.Form; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.auth.oauth.AuthenticationProviderService; +import org.apache.guacamole.auth.oauth.conf.ConfigurationService; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleServerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides relatively abstract means of producing authentication tokens from + * the codes received from OAuth services. + */ +public class TokenService { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); + + /** + * Service for retrieving OAuth configuration information. + */ + @Inject + private ConfigurationService confService; + + /** + * Jersey HTTP client. + */ + @Inject + private Client client; + + /** + * Given an authorization code previously received from the OAuth service + * via the "code" parameter provided to the redirect URL, retrieves and + * returns an authentication token. + * + * @param code + * The value of the "code" parameter received from the OAuth service. + * + * @return + * The authentication roken response received from the OAuth service. + * + * @throws GuacamoleException + * If required properties within guacamole.properties cannot be read, + * or if an error occurs while contacting the OAuth service. + */ + public TokenResponse getTokenFromCode(String code) + throws GuacamoleException { + + try { + + // Generate POST data + Form form = new Form(); + form.add("code", code); + form.add("client_id", confService.getClientID()); + form.add("client_secret", confService.getClientSecret()); + form.add("redirect_uri", confService.getRedirectURI()); + form.add("grant_type", "authorization_code"); + + // POST code and client information to OAuth token endpoint + return client.resource(confService.getTokenEndpoint()) + .type(MediaType.APPLICATION_FORM_URLENCODED_TYPE) + .accept(MediaType.APPLICATION_JSON_TYPE) + .post(TokenResponse.class, form); + + } + + // Log any failure reaching the OAuth service + catch (UniformInterfaceException e) { + logger.debug("POST to token endpoint failed.", e); + throw new GuacamoleServerException("Unable to POST to token endpoint.", e); + } + + } + +} From fdc031338722242e30d1ca0b2e393a4b2ae2e8f0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jun 2016 00:14:00 -0700 Subject: [PATCH 09/28] GUACAMOLE-210: Migrate to implicit flow (client-side, relies on "id_token"). Update to pre-release 0.9.9-incubating codebase. --- extensions/guacamole-auth-openid/pom.xml | 18 +-- .../oauth/AuthenticationProviderService.java | 46 ++---- .../oauth/OAuthAuthenticationProvider.java | 10 +- .../OAuthAuthenticationProviderModule.java | 33 +--- .../auth/oauth/conf/ConfigurationService.java | 38 +---- .../oauth/conf/OAuthGuacamoleProperties.java | 26 +-- ...uthCodeField.java => OAuthTokenField.java} | 31 ++-- .../auth/oauth/token/TokenResponse.java | 153 ------------------ .../auth/oauth/token/TokenService.java | 101 ------------ .../auth/oauth/user/AuthenticatedUser.java | 6 +- .../src/main/resources/guac-manifest.json | 9 +- .../src/main/resources/oauthCodeField.html | 1 - .../src/main/resources/oauthConfig.js | 29 +++- .../src/main/resources/oauthController.js | 30 ++++ 14 files changed, 109 insertions(+), 422 deletions(-) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/{OAuthCodeField.java => OAuthTokenField.java} (74%) delete mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java delete mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java delete mode 100644 extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html create mode 100644 extensions/guacamole-auth-openid/src/main/resources/oauthController.js diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index d443cddee..60691e2d1 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -26,7 +26,7 @@ org.apache.guacamole guacamole-auth-openid jar - 0.9.9 + 0.9.9-incubating guacamole-auth-openid http://guacamole.incubator.apache.org/ @@ -80,24 +80,12 @@ - org.glyptodon.guacamole + org.apache.guacamole guacamole-ext - 0.9.9 + 0.9.9-incubating provided - - - com.sun.jersey - jersey-client - 1.17.1 - - - com.sun.jersey - jersey-json - 1.17.1 - - com.google.inject 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 index 5783faa5d..0aac96849 100644 --- 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 @@ -25,14 +25,12 @@ import java.util.Arrays; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.auth.oauth.form.OAuthCodeField; -import org.apache.guacamole.auth.oauth.token.TokenResponse; -import org.apache.guacamole.auth.oauth.token.TokenService; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.form.Field; -import org.glyptodon.guacamole.net.auth.Credentials; -import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; -import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; +import org.apache.guacamole.auth.oauth.form.OAuthTokenField; +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; @@ -53,12 +51,6 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; - /** - * Service for producing authentication tokens from OAuth codes. - */ - @Inject - private TokenService tokenService; - /** * Provider for AuthenticatedUser objects. */ @@ -83,19 +75,15 @@ public class AuthenticationProviderService { public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { - String code = null; + String token = null; - // Pull OAuth code from request if present + // Pull OAuth token from request if present HttpServletRequest request = credentials.getRequest(); if (request != null) - code = request.getParameter(OAuthCodeField.PARAMETER_NAME); + token = request.getParameter(OAuthTokenField.PARAMETER_NAME); - // TODO: Actually complete authentication using received code - if (code != null) { - - // POST code and client information to OAuth token endpoint - TokenResponse response = tokenService.getTokenFromCode(code); - logger.debug("RESPONSE: {}", response); + // TODO: Actually validate received token + if (token != null) { // Create corresponding authenticated user AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); @@ -104,17 +92,13 @@ public class AuthenticationProviderService { } - // Request auth code + // Request OAuth token throw new GuacamoleInvalidCredentialsException("Invalid login.", new CredentialsInfo(Arrays.asList(new Field[] { - // Normal username/password fields - CredentialsInfo.USERNAME, - CredentialsInfo.PASSWORD, - - // OAuth-specific code (will be rendered as an appropriate - // "Log in with..." button - new OAuthCodeField( + // OAuth-specific token (will automatically redirect the user + // to the authorization page via JavaScript) + new OAuthTokenField( confService.getAuthorizationEndpoint(), confService.getClientID(), confService.getRedirectURI() 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 index 06255ac80..6ede89071 100644 --- 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 @@ -21,11 +21,11 @@ package org.apache.guacamole.auth.oauth; import com.google.inject.Guice; import com.google.inject.Injector; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.AuthenticatedUser; -import org.glyptodon.guacamole.net.auth.AuthenticationProvider; -import org.glyptodon.guacamole.net.auth.Credentials; -import org.glyptodon.guacamole.net.auth.UserContext; +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 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 index a5cef6da5..202e6a267 100644 --- 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 @@ -20,17 +20,11 @@ package org.apache.guacamole.auth.oauth; import com.google.inject.AbstractModule; -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.config.ClientConfig; -import com.sun.jersey.api.client.config.DefaultClientConfig; import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.apache.guacamole.auth.oauth.token.TokenService; -import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider; -import org.codehaus.jackson.map.DeserializationConfig; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.environment.Environment; -import org.glyptodon.guacamole.environment.LocalEnvironment; -import org.glyptodon.guacamole.net.auth.AuthenticationProvider; +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. @@ -48,12 +42,6 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { */ private final AuthenticationProvider authProvider; - /** - * A reference to the shared HTTP client to be used when making calls to - * the OAuth service. - */ - private final Client client; - /** * Creates a new OAuth authentication provider module which configures * injection for the OAuthAuthenticationProvider. @@ -74,15 +62,6 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { // Store associated auth provider this.authProvider = authProvider; - // Set up configuration for HTTP client - ClientConfig clientConfig = new DefaultClientConfig(); - clientConfig.getSingletons().add(new JacksonJaxbJsonProvider() - .configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) - ); - - // Store pre-configured HTTP client - this.client = Client.create(clientConfig); - } @Override @@ -94,10 +73,6 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { // Bind OAuth-specific services bind(ConfigurationService.class); - bind(TokenService.class); - - // Bind HTTP client - bind(Client.class).toInstance(client); } 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 index e1567d239..9debab77b 100644 --- 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 @@ -20,8 +20,8 @@ package org.apache.guacamole.auth.oauth.conf; import com.google.inject.Inject; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.environment.Environment; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; /** * Service for retrieving configuration information regarding the OAuth service. @@ -50,22 +50,6 @@ public class ConfigurationService { return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_AUTHORIZATION_ENDPOINT); } - /** - * Returns the token endpoint (URI) of the OAuth service as configured with - * guacamole.properties. - * - * @return - * The token 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 getTokenEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_TOKEN_ENDPOINT); - } - /** * Returns the OAuth client ID which should be submitted to the OAuth * service when necessary, as configured with guacamole.properties. This @@ -84,24 +68,6 @@ public class ConfigurationService { return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_CLIENT_ID); } - /** - * Returns the OAuth client secret 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 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 client secret - * property is missing. - */ - public String getClientSecret() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_CLIENT_SECRET); - } - /** * Returns the URI that the OAuth service should redirect to after * the authentication process is complete, as configured with 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 index 0ebb94f3a..34952fe3a 100644 --- 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 @@ -19,7 +19,7 @@ package org.apache.guacamole.auth.oauth.conf; -import org.glyptodon.guacamole.properties.StringGuacamoleProperty; +import org.apache.guacamole.properties.StringGuacamoleProperty; /** * Provides properties required for use of the OAuth authentication provider. @@ -44,17 +44,6 @@ public class OAuthGuacamoleProperties { }; - /** - * The token endpoint (URI) of the OAuth service. - */ - public static final StringGuacamoleProperty OAUTH_TOKEN_ENDPOINT = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-token-endpoint"; } - - }; - /** * OAuth client ID which should be submitted to the OAuth service when * necessary. This value is typically provided by the OAuth service when @@ -68,19 +57,6 @@ public class OAuthGuacamoleProperties { }; - /** - * OAuth client secret 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_SECRET = - new StringGuacamoleProperty() { - - @Override - public String getName() { return "oauth-client-secret"; } - - }; - /** * The URI that the OAuth service should redirect to after the * authentication process is complete. This must be the full URL that a diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java similarity index 74% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java index 9b0764a5e..84484e5e8 100644 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthCodeField.java +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java @@ -21,20 +21,21 @@ package org.apache.guacamole.auth.oauth.form; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import org.glyptodon.guacamole.form.Field; +import java.util.UUID; +import org.apache.guacamole.form.Field; /** - * Field definition which represents the code returned by an OAuth service. + * 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 OAuthCodeField extends Field { +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 = "code"; + public static final String PARAMETER_NAME = "id_token"; /** * The full URI which the field should link to. @@ -42,11 +43,12 @@ public class OAuthCodeField extends Field { private final String authorizationURI; /** - * Creates a new OAuth "code" 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 code will be embedded in the query parameters of - * that URI. + * 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 @@ -61,19 +63,20 @@ public class OAuthCodeField extends Field { * The URI that the OAuth service should redirect to upon successful * authentication. */ - public OAuthCodeField(String authorizationEndpoint, String clientID, + public OAuthTokenField(String authorizationEndpoint, String clientID, String redirectURI) { // Init base field properties - super(PARAMETER_NAME, "GUAC_OAUTH_CODE"); + super(PARAMETER_NAME, "GUAC_OAUTH_TOKEN"); // Build authorization URI from given values try { this.authorizationURI = authorizationEndpoint + "?scope=openid%20email%20profile" - + "&response_type=code" + + "&response_type=id_token" + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") - + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8"); + + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") + + "&nonce=" + UUID.randomUUID().toString(); } // Java is required to provide UTF-8 support @@ -84,7 +87,7 @@ public class OAuthCodeField extends Field { } /** - * Returns the full URI that this field should link to when a new code + * Returns the full URI that this field should link to when a new token * needs to be obtained from the OAuth service. * * @return diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java deleted file mode 100644 index 513683041..000000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenResponse.java +++ /dev/null @@ -1,153 +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 org.codehaus.jackson.annotate.JsonProperty; - -/** - * The response produced from a successful request to the token endpoint of an - * OAuth service. - */ -public class TokenResponse { - - /** - * An arbitrary access token which can be used for future requests against - * the API associated with the OAuth service. - */ - private String accessToken; - - /** - * The type of token present. This will always be "Bearer". - */ - private String tokenType; - - /** - * The number of seconds the access token will remain valid. - */ - private int expiresIn; - - /** - * A JWT (JSON Web Token) which containing identity information which has - * been cryptographically signed. - */ - private String idToken; - - /** - * Returns an arbitrary access token which can be used for future requests - * against the API associated with the OAuth service. - * - * @return - * An arbitrary access token provided by the OAuth service. - */ - @JsonProperty("access_token") - public String getAccessToken() { - return accessToken; - } - - /** - * Sets the arbitrary access token which can be used for future requests - * against the API associated with the OAuth service. - * - * @param accessToken - * The arbitrary access token provided by the OAuth service. - */ - @JsonProperty("access_token") - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - /** - * Returns the type of token present in this response. This should always - * be "Bearer". - * - * @return - * The type of token present in this response. - */ - @JsonProperty("token_type") - public String getTokenType() { - return tokenType; - } - - /** - * Sets the type of token present in this response. This should always be - * "Bearer". - * - * @param tokenType - * The type of token present in this response, which should be - * "Bearer". - */ - @JsonProperty("token_type") - public void setTokenType(String tokenType) { - this.tokenType = tokenType; - } - - /** - * Returns the number of seconds the access token within this response will - * remain valid. - * - * @return - * The number of seconds the access token within this response will - * remain valid. - */ - @JsonProperty("expires_in") - public int getExpiresIn() { - return expiresIn; - } - - /** - * Sets the number of seconds the access token within this response will - * remain valid. - * - * @param expiresIn - * The number of seconds the access token within this response will - * remain valid. - */ - @JsonProperty("expires_in") - public void setExpiresIn(int expiresIn) { - this.expiresIn = expiresIn; - } - - /** - * Returns a JWT (JSON Web Token) containing identity information which has - * been cryptographically signed by the OAuth service. - * - * @return - * A JWT (JSON Web Token) containing identity information which has - * been cryptographically signed by the OAuth service. - */ - @JsonProperty("id_token") - public String getIdToken() { - return idToken; - } - - /** - * Sets the JWT (JSON Web Token) containing identity information which has - * been cryptographically signed by the OAuth service. - * - * @param idToken - * A JWT (JSON Web Token) containing identity information which has - * been cryptographically signed by the OAuth service. - */ - @JsonProperty("id_token") - public void setIdToken(String idToken) { - this.idToken = idToken; - } - -} diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java deleted file mode 100644 index a328bde4d..000000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenService.java +++ /dev/null @@ -1,101 +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 com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.UniformInterfaceException; -import com.sun.jersey.api.representation.Form; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.auth.oauth.AuthenticationProviderService; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleServerException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides relatively abstract means of producing authentication tokens from - * the codes received from OAuth services. - */ -public class TokenService { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); - - /** - * Service for retrieving OAuth configuration information. - */ - @Inject - private ConfigurationService confService; - - /** - * Jersey HTTP client. - */ - @Inject - private Client client; - - /** - * Given an authorization code previously received from the OAuth service - * via the "code" parameter provided to the redirect URL, retrieves and - * returns an authentication token. - * - * @param code - * The value of the "code" parameter received from the OAuth service. - * - * @return - * The authentication roken response received from the OAuth service. - * - * @throws GuacamoleException - * If required properties within guacamole.properties cannot be read, - * or if an error occurs while contacting the OAuth service. - */ - public TokenResponse getTokenFromCode(String code) - throws GuacamoleException { - - try { - - // Generate POST data - Form form = new Form(); - form.add("code", code); - form.add("client_id", confService.getClientID()); - form.add("client_secret", confService.getClientSecret()); - form.add("redirect_uri", confService.getRedirectURI()); - form.add("grant_type", "authorization_code"); - - // POST code and client information to OAuth token endpoint - return client.resource(confService.getTokenEndpoint()) - .type(MediaType.APPLICATION_FORM_URLENCODED_TYPE) - .accept(MediaType.APPLICATION_JSON_TYPE) - .post(TokenResponse.class, form); - - } - - // Log any failure reaching the OAuth service - catch (UniformInterfaceException e) { - logger.debug("POST to token endpoint failed.", e); - throw new GuacamoleServerException("Unable to POST to token endpoint.", e); - } - - } - -} 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 index 935c27070..3a798eb4a 100644 --- 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 @@ -20,9 +20,9 @@ package org.apache.guacamole.auth.oauth.user; import com.google.inject.Inject; -import org.glyptodon.guacamole.net.auth.AbstractAuthenticatedUser; -import org.glyptodon.guacamole.net.auth.AuthenticationProvider; -import org.glyptodon.guacamole.net.auth.Credentials; +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 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 e8f2facc1..cc74547df 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -1,6 +1,6 @@ { - "guacamoleVersion" : "0.9.9", + "guacamoleVersion" : "0.9.9-incubating", "name" : "OAuth Authentication Extension", "namespace" : "guac-oauth", @@ -11,11 +11,8 @@ "js" : [ "oauthModule.js", + "oauthController.js", "oauthConfig.js" - ], - - "resources" : { - "oauthCodeField.html" : "text/html" - } + ] } diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html b/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html deleted file mode 100644 index e6c4fff04..000000000 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthCodeField.html +++ /dev/null @@ -1 +0,0 @@ -Log in using OAuth \ No newline at end of file diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js b/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js index ba6f0cc88..431965697 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js +++ b/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js @@ -23,9 +23,32 @@ angular.module('guacOAuth').config(['formServiceProvider', function guacOAuthConfig(formServiceProvider) { - // Define field for code from OAuth service - formServiceProvider.registerFieldType("GUAC_OAUTH_CODE", { - templateUrl : 'app/ext/guac-oauth/oauthCodeField.html' + // 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); + }] + }); }]); diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthController.js b/extensions/guacamole-auth-openid/src/main/resources/oauthController.js new file mode 100644 index 000000000..ba7a1208d --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/oauthController.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_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; + +}]); From d27ba44439e702964cb668886ccbc35f740b38e8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 12 Jun 2016 23:03:47 -0700 Subject: [PATCH 10/28] GUACAMOLE-210: Validate the JWT using jose.4.j. --- extensions/guacamole-auth-openid/pom.xml | 7 ++ .../oauth/AuthenticationProviderService.java | 13 ++- .../OAuthAuthenticationProviderModule.java | 2 + .../auth/oauth/conf/ConfigurationService.java | 52 ++++++++- .../oauth/conf/OAuthGuacamoleProperties.java | 35 ++++++ .../oauth/token/TokenValidationService.java | 102 ++++++++++++++++++ 6 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 60691e2d1..fa819c80b 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -86,6 +86,13 @@ provided + + + org.bitbucket.b_c + jose4j + 0.5.1 + + com.google.inject 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 index 0aac96849..d89f08797 100644 --- 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 @@ -23,9 +23,10 @@ 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.user.AuthenticatedUser; 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; @@ -51,6 +52,12 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; + /** + * Service for validating received ID tokens. + */ + @Inject + private TokenValidationService tokenService; + /** * Provider for AuthenticatedUser objects. */ @@ -82,12 +89,12 @@ public class AuthenticationProviderService { if (request != null) token = request.getParameter(OAuthTokenField.PARAMETER_NAME); - // TODO: Actually validate received token + // If token provided, validate and produce authenticated user if (token != null) { // Create corresponding authenticated user AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init("STUB", credentials); + authenticatedUser.init(tokenService.processUsername(token), credentials); return authenticatedUser; } 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 index 202e6a267..f83806319 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -73,6 +74,7 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { // Bind OAuth-specific services bind(ConfigurationService.class); + bind(TokenValidationService.class); } 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 index 9debab77b..1304d5898 100644 --- 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 @@ -79,11 +79,61 @@ public class ConfigurationService { * as configured with guacamole.properties. * * @throws GuacamoleException - * If guacamole.properties cannot be parsed, or if the client secret + * 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); + } + } 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 index 34952fe3a..cfb4eb37a 100644 --- 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 @@ -44,6 +44,41 @@ public class OAuthGuacamoleProperties { }; + /** + * 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 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 new file mode 100644 index 000000000..a61f7ceee --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.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 { + + @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); + } + + } + +} From 9159ca4289cc1a13d78afdea17067c64b7ea27d8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 13 Jun 2016 00:01:08 -0700 Subject: [PATCH 11/28] GUACAMOLE-210: Use cryptographically-sound nonce generator. --- .../auth/oauth/form/OAuthTokenField.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) 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 index 84484e5e8..5d6599fc4 100644 --- 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 @@ -20,8 +20,9 @@ package org.apache.guacamole.auth.oauth.form; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.net.URLEncoder; -import java.util.UUID; +import java.security.SecureRandom; import org.apache.guacamole.form.Field; /** @@ -42,6 +43,23 @@ public class OAuthTokenField extends Field { */ 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 @@ -76,7 +94,7 @@ public class OAuthTokenField extends Field { + "&response_type=id_token" + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") - + "&nonce=" + UUID.randomUUID().toString(); + + "&nonce=" + generateNonce(); } // Java is required to provide UTF-8 support From faa327824beca658cbf0cb199d1bf049921b03a9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 13 Jun 2016 02:19:05 -0700 Subject: [PATCH 12/28] GUACAMOLE-210: Add missing comment. --- .../guacamole/auth/oauth/token/TokenValidationService.java | 3 +++ 1 file changed, 3 insertions(+) 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 index a61f7ceee..84bfa3d19 100644 --- 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 @@ -38,6 +38,9 @@ import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver; */ public class TokenValidationService { + /** + * Service for retrieving OAuth configuration information. + */ @Inject private ConfigurationService confService; From c5bd3390bfd60c62ea199faa73ef6fb0bb963ffd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 14 Aug 2016 19:25:11 -0700 Subject: [PATCH 13/28] GUACAMOLE-210: Update for recent sharing-related changes to AuthenticationProvider on 0.9.9-incubating (current upstream git). --- .../guacamole/auth/oauth/OAuthAuthenticationProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 6ede89071..6ecfeb574 100644 --- 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 @@ -94,7 +94,8 @@ public class OAuthAuthenticationProvider implements AuthenticationProvider { @Override public UserContext updateUserContext(UserContext context, - AuthenticatedUser authenticatedUser) throws GuacamoleException { + AuthenticatedUser authenticatedUser, Credentials credentials) + throws GuacamoleException { // No update necessary return context; From 254639f6e9d4a2f48b971245e69a74ae42ac9dd4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 19 Feb 2017 11:06:49 -0800 Subject: [PATCH 14/28] GUACAMOLE-210: Update to 0.9.11-incubating API. --- extensions/guacamole-auth-openid/pom.xml | 4 ++-- .../src/main/resources/guac-manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index fa819c80b..eec594109 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -26,7 +26,7 @@ org.apache.guacamole guacamole-auth-openid jar - 0.9.9-incubating + 0.9.11-incubating guacamole-auth-openid http://guacamole.incubator.apache.org/ @@ -82,7 +82,7 @@ org.apache.guacamole guacamole-ext - 0.9.9-incubating + 0.9.11-incubating provided 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 cc74547df..add9607f1 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -1,6 +1,6 @@ { - "guacamoleVersion" : "0.9.9-incubating", + "guacamoleVersion" : "0.9.11-incubating", "name" : "OAuth Authentication Extension", "namespace" : "guac-oauth", From 6d46d5cfb8307f2f57e14261855e9a72f555c0c4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 11:24:33 -0800 Subject: [PATCH 15/28] GUACAMOLE-210: Add Apache RAT plugin to guacamole-auth-openid build. --- extensions/guacamole-auth-openid/pom.xml | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index eec594109..698bc12cb 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -73,6 +73,32 @@ + + + org.apache.rat + apache-rat-plugin + 0.12 + + + + **/*.json + src/licenses/**/* + + + + + + + validate + validate + + check + + + + + + From 11fb9b3fa432a8e02037e403fbb6f72d6a39a78d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 11:35:47 -0800 Subject: [PATCH 16/28] GUACAMOLE-210: Update to jose4j 0.5.5. --- extensions/guacamole-auth-openid/pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 698bc12cb..13de21497 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -116,7 +116,13 @@ org.bitbucket.b_c jose4j - 0.5.1 + 0.5.5 + + + org.slf4j + slf4j-api + + From a8f97b548ece86e866e5ea96e0e49047270e34b7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 11:42:19 -0800 Subject: [PATCH 17/28] GUACAMOLE-210: Document licenses of bundled dependencies for guacamole-auth-openid. --- .../src/licenses/DISCLAIMER | 7 + .../src/licenses/LICENSE | 248 ++++++++++++++++++ .../guacamole-auth-openid/src/licenses/NOTICE | 5 + .../src/licenses/bundled/README | 4 + .../licenses/bundled/aopalliance-1.0/LICENSE | 4 + .../src/licenses/bundled/guice-3.0/COPYING | 202 ++++++++++++++ .../bundled/javax.inject-1/LICENSE-2.0.txt | 202 ++++++++++++++ .../src/licenses/bundled/jose4j-0.5.5/LICENSE | 202 ++++++++++++++ .../licenses/bundled/jose4j-0.5.5/NOTICE.txt | 14 + 9 files changed, 888 insertions(+) create mode 100644 extensions/guacamole-auth-openid/src/licenses/DISCLAIMER create mode 100644 extensions/guacamole-auth-openid/src/licenses/LICENSE create mode 100644 extensions/guacamole-auth-openid/src/licenses/NOTICE create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/README create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/aopalliance-1.0/LICENSE create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/guice-3.0/COPYING create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/LICENSE create mode 100644 extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/NOTICE.txt diff --git a/extensions/guacamole-auth-openid/src/licenses/DISCLAIMER b/extensions/guacamole-auth-openid/src/licenses/DISCLAIMER new file mode 100644 index 000000000..1a9c3be8d --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/DISCLAIMER @@ -0,0 +1,7 @@ +Apache Guacamole is an effort undergoing incubation at The Apache Software +Foundation (ASF). Incubation is required of all newly accepted projects until a +further review indicates that the infrastructure, communications, and decision +making process have stabilized in a manner consistent with other successful ASF +projects. While incubation status is not necessarily a reflection of the +completeness or stability of the code, it does indicate that the project has +yet to be fully endorsed by the ASF. diff --git a/extensions/guacamole-auth-openid/src/licenses/LICENSE b/extensions/guacamole-auth-openid/src/licenses/LICENSE new file mode 100644 index 000000000..f95ed290f --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/LICENSE @@ -0,0 +1,248 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + +============================================================================== + +APACHE GUACAMOLE SUBCOMPONENTS + +Apache Guacamole includes a number of subcomponents with separate copyright +notices and license terms. Your use of these subcomponents is subject to the +terms and conditions of the following licenses. + + +AOP Alliance (http://aopalliance.sourceforge.net/) +-------------------------------------------------- + + Version: 1.0 + From: 'AOP Alliance' (http://aopalliance.sourceforge.net/members.html) + License(s): + Public Domain (bundled/aopalliance-1.0/LICENSE) + + +Google Guice (https://github.com/google/guice) +---------------------------------------------- + + Version: 3.0 + From: 'Google Inc.' (http://www.google.com/) + License(s): + Apache v2.0 (bundled/guice-3.0/COPYING) + + +jose.4.j (https://bitbucket.org/b_c/jose4j/) +-------------------------------------------- + + Version: 0.5.5 + From: 'Brian Campbell' (https://bitbucket.org/b_c/) + License(s): + Apache v2.0 (bundled/jose4j-0.5.5/LICENSE) + + +JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/) +---------------------------------------------------------------------------- + + Version: 1 + From: 'JSR-330 Expert Group' (https://jcp.org/en/jsr/detail?id=330) + License(s): + Apache v2.0 (bundled/javax.inject-1/LICENSE-2.0.txt) + diff --git a/extensions/guacamole-auth-openid/src/licenses/NOTICE b/extensions/guacamole-auth-openid/src/licenses/NOTICE new file mode 100644 index 000000000..2ef7e548b --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/NOTICE @@ -0,0 +1,5 @@ +Apache Guacamole +Copyright 2016 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/README b/extensions/guacamole-auth-openid/src/licenses/bundled/README new file mode 100644 index 000000000..47ba19db0 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/README @@ -0,0 +1,4 @@ +Apache Guacamole includes a number of subcomponents with separate copyright +notices and license terms. Your use of these subcomponents is subject to the +terms and conditions of their respective licenses, included within this +directory for reference. diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/aopalliance-1.0/LICENSE b/extensions/guacamole-auth-openid/src/licenses/bundled/aopalliance-1.0/LICENSE new file mode 100644 index 000000000..8e0e3786b --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/aopalliance-1.0/LICENSE @@ -0,0 +1,4 @@ +From http://aopalliance.sourceforge.net/: + + LICENCE: all the source code provided by AOP Alliance is Public Domain. + diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/guice-3.0/COPYING b/extensions/guacamole-auth-openid/src/licenses/bundled/guice-3.0/COPYING new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/guice-3.0/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt b/extensions/guacamole-auth-openid/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/javax.inject-1/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/LICENSE b/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/NOTICE.txt b/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/NOTICE.txt new file mode 100644 index 000000000..46498244b --- /dev/null +++ b/extensions/guacamole-auth-openid/src/licenses/bundled/jose4j-0.5.5/NOTICE.txt @@ -0,0 +1,14 @@ +jose4j +Copyright 2012-2015 Brian Campbell + +EcdsaUsingShaAlgorithm contains code for converting the concatenated +R & S values of the signature to and from DER, which was originally +derived from the Apache Santuario XML Security library's SignatureECDSA +implementation. http://santuario.apache.org/ + +The Base64 implementation in this software was derived from the +Apache Commons Codec project. http://commons.apache.org/proper/commons-codec/ + +JSON processing in this software was derived from the JSON.simple toolkit. +https://code.google.com/p/json-simple/ + From 1034612a47b8fdea4b1e8ee710615d09cd85d06d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 11:43:35 -0800 Subject: [PATCH 18/28] GUACAMOLE-210: Create guacamole-auth-openid bundle .tar.gz as part of build. --- extensions/guacamole-auth-openid/pom.xml | 22 ++++++++ .../src/main/assembly/dist.xml | 53 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 extensions/guacamole-auth-openid/src/main/assembly/dist.xml diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 13de21497..89f5b6cbd 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -73,6 +73,28 @@ + + + maven-assembly-plugin + 2.5.3 + + ${project.artifactId}-${project.version} + false + + src/main/assembly/dist.xml + + + + + make-dist-archive + package + + single + + + + + org.apache.rat diff --git a/extensions/guacamole-auth-openid/src/main/assembly/dist.xml b/extensions/guacamole-auth-openid/src/main/assembly/dist.xml new file mode 100644 index 000000000..b89fd534c --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/assembly/dist.xml @@ -0,0 +1,53 @@ + + + + + dist + ${project.artifactId}-${project.version} + + + + tar.gz + + + + + + + + + src/licenses + + + + + target + + + *.jar + + + + + + From d04d61225a9f820b99fd1815c5b24205dc1cc8e1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 12:43:15 -0800 Subject: [PATCH 19/28] GUACAMOLE-210: Refactor source referencing OAuth to OpenID. This extension uses OpenID, not OAuth. --- .../AuthenticationProviderService.java | 24 +++++----- .../OpenIDAuthenticationProvider.java} | 16 +++---- .../OpenIDAuthenticationProviderModule.java} | 20 ++++----- .../conf/ConfigurationService.java | 31 ++++++------- .../conf/OpenIDGuacamoleProperties.java} | 44 +++++++++---------- .../form/TokenField.java} | 30 ++++++------- .../token/TokenValidationService.java | 8 ++-- .../user/AuthenticatedUser.java | 6 +-- .../src/main/resources/guac-manifest.json | 12 ++--- .../{oauthConfig.js => openidConfig.js} | 14 +++--- ...oauthController.js => openidController.js} | 6 +-- .../{oauthModule.js => openidModule.js} | 8 ++-- 12 files changed, 110 insertions(+), 109 deletions(-) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth => openid}/AuthenticationProviderService.java (82%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth/OAuthAuthenticationProvider.java => openid/OpenIDAuthenticationProvider.java} (86%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth/OAuthAuthenticationProviderModule.java => openid/OpenIDAuthenticationProviderModule.java} (76%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth => openid}/conf/ConfigurationService.java (78%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth/conf/OAuthGuacamoleProperties.java => openid/conf/OpenIDGuacamoleProperties.java} (59%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth/form/OAuthTokenField.java => openid/form/TokenField.java} (76%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth => openid}/token/TokenValidationService.java (94%) rename extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/{oauth => openid}/user/AuthenticatedUser.java (91%) rename extensions/guacamole-auth-openid/src/main/resources/{oauthConfig.js => openidConfig.js} (79%) rename extensions/guacamole-auth-openid/src/main/resources/{oauthController.js => openidController.js} (82%) rename extensions/guacamole-auth-openid/src/main/resources/{oauthModule.js => openidModule.js} (79%) 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/openid/AuthenticationProviderService.java similarity index 82% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java index d89f08797..10dea3d43 100644 --- 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/openid/AuthenticationProviderService.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +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.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.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; @@ -36,7 +36,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Service providing convenience functions for the OAuth AuthenticationProvider + * Service providing convenience functions for the OpenID AuthenticationProvider * implementation. */ public class AuthenticationProviderService { @@ -47,7 +47,7 @@ public class AuthenticationProviderService { private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); /** - * Service for retrieving OAuth configuration information. + * Service for retrieving OpenID configuration information. */ @Inject private ConfigurationService confService; @@ -84,10 +84,10 @@ public class AuthenticationProviderService { String token = null; - // Pull OAuth token from request if present + // Pull OpenID token from request if present HttpServletRequest request = credentials.getRequest(); if (request != null) - token = request.getParameter(OAuthTokenField.PARAMETER_NAME); + token = request.getParameter(TokenField.PARAMETER_NAME); // If token provided, validate and produce authenticated user if (token != null) { @@ -99,13 +99,13 @@ public class AuthenticationProviderService { } - // Request OAuth token + // Request OpenID token throw new GuacamoleInvalidCredentialsException("Invalid login.", new CredentialsInfo(Arrays.asList(new Field[] { - // OAuth-specific token (will automatically redirect the user + // OpenID-specific token (will automatically redirect the user // to the authorization page via JavaScript) - new OAuthTokenField( + new TokenField( confService.getAuthorizationEndpoint(), confService.getClientID(), confService.getRedirectURI() 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/openid/OpenIDAuthenticationProvider.java similarity index 86% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProvider.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProvider.java index 6ecfeb574..7fa854832 100644 --- 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/openid/OpenIDAuthenticationProvider.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +package org.apache.guacamole.auth.openid; import com.google.inject.Guice; import com.google.inject.Injector; @@ -29,11 +29,11 @@ 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 + * arbitrary external system implementing OpenID. No storage for connections is * provided - only authentication. Storage must be provided by some other * extension. */ -public class OAuthAuthenticationProvider implements AuthenticationProvider { +public class OpenIDAuthenticationProvider implements AuthenticationProvider { /** * Injector which will manage the object graph of this authentication @@ -42,25 +42,25 @@ public class OAuthAuthenticationProvider implements AuthenticationProvider { private final Injector injector; /** - * Creates a new OAuthAuthenticationProvider that authenticates users - * against an OAuth service + * 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 OAuthAuthenticationProvider() throws GuacamoleException { + public OpenIDAuthenticationProvider() throws GuacamoleException { // Set up Guice injector. injector = Guice.createInjector( - new OAuthAuthenticationProviderModule(this) + new OpenIDAuthenticationProviderModule(this) ); } @Override public String getIdentifier() { - return "oauth"; + return "openid"; } @Override 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/openid/OpenIDAuthenticationProviderModule.java similarity index 76% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java index f83806319..9abd6667f 100644 --- 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/openid/OpenIDAuthenticationProviderModule.java @@ -17,20 +17,20 @@ * under the License. */ -package org.apache.guacamole.auth.oauth; +package org.apache.guacamole.auth.openid; 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.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 OAuth-specific injections. + * Guice module which configures openid-specific injections. */ -public class OAuthAuthenticationProviderModule extends AbstractModule { +public class OpenIDAuthenticationProviderModule extends AbstractModule { /** * Guacamole server environment. @@ -38,14 +38,14 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { private final Environment environment; /** - * A reference to the OAuthAuthenticationProvider on behalf of which this + * A reference to the OpenIDAuthenticationProvider 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. + * Creates a new OpenID authentication provider module which configures + * injection for the OpenIDAuthenticationProvider. * * @param authProvider * The AuthenticationProvider for which injection is being configured. @@ -54,7 +54,7 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { * If an error occurs while retrieving the Guacamole server * environment. */ - public OAuthAuthenticationProviderModule(AuthenticationProvider authProvider) + public OpenIDAuthenticationProviderModule(AuthenticationProvider authProvider) throws GuacamoleException { // Get local environment @@ -72,7 +72,7 @@ public class OAuthAuthenticationProviderModule extends AbstractModule { bind(AuthenticationProvider.class).toInstance(authProvider); bind(Environment.class).toInstance(environment); - // Bind OAuth-specific services + // Bind openid-specific services bind(ConfigurationService.class); bind(TokenValidationService.class); 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/openid/conf/ConfigurationService.java similarity index 78% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java index 1304d5898..650cf47f4 100644 --- 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/openid/conf/ConfigurationService.java @@ -17,14 +17,15 @@ * under the License. */ -package org.apache.guacamole.auth.oauth.conf; +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 OAuth service. + * Service for retrieving configuration information regarding the OpenID + * service. */ public class ConfigurationService { @@ -35,11 +36,11 @@ public class ConfigurationService { private Environment environment; /** - * Returns the authorization endpoint (URI) of the OAuth service as + * Returns the authorization endpoint (URI) of the OpenID service as * configured with guacamole.properties. * * @return - * The authorization endpoint of the OAuth service, as configured with + * The authorization endpoint of the OpenID service, as configured with * guacamole.properties. * * @throws GuacamoleException @@ -47,17 +48,17 @@ public class ConfigurationService { * endpoint property is missing. */ public String getAuthorizationEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_AUTHORIZATION_ENDPOINT); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_AUTHORIZATION_ENDPOINT); } /** - * Returns the OAuth client ID which should be submitted to the OAuth + * 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 OAuth service when OAuth credentials + * 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 OAuth service, + * The client ID to use when communicating with the OpenID service, * as configured with guacamole.properties. * * @throws GuacamoleException @@ -65,17 +66,17 @@ public class ConfigurationService { * property is missing. */ public String getClientID() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_CLIENT_ID); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_CLIENT_ID); } /** - * Returns the URI that the OAuth service should redirect to after + * 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 OAuth service, + * The client secret to use when communicating with the OpenID service, * as configured with guacamole.properties. * * @throws GuacamoleException @@ -83,7 +84,7 @@ public class ConfigurationService { * property is missing. */ public String getRedirectURI() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_REDIRECT_URI); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_REDIRECT_URI); } /** @@ -99,7 +100,7 @@ public class ConfigurationService { * is missing. */ public String getIssuer() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_ISSUER); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_ISSUER); } /** @@ -117,7 +118,7 @@ public class ConfigurationService { * property is missing. */ public String getJWKSEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_JWKS_ENDPOINT); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_JWKS_ENDPOINT); } /** @@ -133,7 +134,7 @@ public class ConfigurationService { * type property is missing. */ public String getUsernameClaimType() throws GuacamoleException { - return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_USERNAME_CLAIM_TYPE); + return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_USERNAME_CLAIM_TYPE); } } 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/openid/conf/OpenIDGuacamoleProperties.java similarity index 59% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.java index cfb4eb37a..2049ccaf5 100644 --- 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/openid/conf/OpenIDGuacamoleProperties.java @@ -17,30 +17,30 @@ * under the License. */ -package org.apache.guacamole.auth.oauth.conf; +package org.apache.guacamole.auth.openid.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 + * 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 OAuthGuacamoleProperties { +public class OpenIDGuacamoleProperties { /** * This class should not be instantiated. */ - private OAuthGuacamoleProperties() {} + private OpenIDGuacamoleProperties() {} /** - * The authorization endpoint (URI) of the OAuth service. + * The authorization endpoint (URI) of the OpenID service. */ - public static final StringGuacamoleProperty OAUTH_AUTHORIZATION_ENDPOINT = + public static final StringGuacamoleProperty OPENID_AUTHORIZATION_ENDPOINT = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-authorization-endpoint"; } + public String getName() { return "openid-authorization-endpoint"; } }; @@ -48,22 +48,22 @@ public class OAuthGuacamoleProperties { * The endpoint (URI) of the JWKS service which defines how received ID * tokens (JWTs) shall be validated. */ - public static final StringGuacamoleProperty OAUTH_JWKS_ENDPOINT = + public static final StringGuacamoleProperty OPENID_JWKS_ENDPOINT = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-jwks-endpoint"; } + public String getName() { return "openid-jwks-endpoint"; } }; /** * The issuer to expect for all received ID tokens. */ - public static final StringGuacamoleProperty OAUTH_ISSUER = + public static final StringGuacamoleProperty OPENID_ISSUER = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-issuer"; } + public String getName() { return "openid-issuer"; } }; @@ -71,37 +71,37 @@ public class OAuthGuacamoleProperties { * The claim type which contains the authenticated user's username within * any valid JWT. */ - public static final StringGuacamoleProperty OAUTH_USERNAME_CLAIM_TYPE = + public static final StringGuacamoleProperty OPENID_USERNAME_CLAIM_TYPE = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-username-claim-type"; } + public String getName() { return "openid-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. + * 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 OAUTH_CLIENT_ID = + public static final StringGuacamoleProperty OPENID_CLIENT_ID = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-client-id"; } + public String getName() { return "openid-client-id"; } }; /** - * The URI that the OAuth service should redirect to after the + * 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 OAUTH_REDIRECT_URI = + public static final StringGuacamoleProperty OPENID_REDIRECT_URI = new StringGuacamoleProperty() { @Override - public String getName() { return "oauth-redirect-uri"; } + public String getName() { return "openid-redirect-uri"; } }; 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/openid/form/TokenField.java similarity index 76% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/form/OAuthTokenField.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java index 5d6599fc4..3ef5d9404 100644 --- 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/openid/form/TokenField.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth.form; +package org.apache.guacamole.auth.openid.form; import java.io.UnsupportedEncodingException; import java.math.BigInteger; @@ -26,15 +26,15 @@ import java.security.SecureRandom; import org.apache.guacamole.form.Field; /** - * Field definition which represents the token returned by an OAuth service. + * 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 OAuth service. + * with ..." button which links to the OpenID service. */ -public class OAuthTokenField extends Field { +public class TokenField extends Field { /** * The standard HTTP parameter which will be included within the URL by all - * OAuth services upon successful authentication and redirect. + * OpenID services upon successful authentication and redirect. */ public static final String PARAMETER_NAME = "id_token"; @@ -61,31 +61,31 @@ public class OAuthTokenField extends Field { } /** - * Creates a new OAuth "id_token" field which links to the given OAuth + * Creates a new OpenID "id_token" field which links to the given OpenID * 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 + * 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 OAuth authentication + * The full URL of the endpoint accepting OpenID 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 + * 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 OAuth service should redirect to upon successful + * The URI that the OpenID service should redirect to upon successful * authentication. */ - public OAuthTokenField(String authorizationEndpoint, String clientID, + public TokenField(String authorizationEndpoint, String clientID, String redirectURI) { // Init base field properties - super(PARAMETER_NAME, "GUAC_OAUTH_TOKEN"); + super(PARAMETER_NAME, "GUAC_OPENID_TOKEN"); // Build authorization URI from given values try { @@ -106,7 +106,7 @@ public class OAuthTokenField extends Field { /** * Returns the full URI that this field should link to when a new token - * needs to be obtained from the OAuth service. + * needs to be obtained from the OpenID service. * * @return * The full URI that this field should link to. 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/openid/token/TokenValidationService.java similarity index 94% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java index 84bfa3d19..b1a8a28f7 100644 --- 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/openid/token/TokenValidationService.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.guacamole.auth.oauth.token; +package org.apache.guacamole.auth.openid.token; import com.google.inject.Inject; -import org.apache.guacamole.auth.oauth.conf.ConfigurationService; +import org.apache.guacamole.auth.openid.conf.ConfigurationService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleServerException; @@ -34,12 +34,12 @@ 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. + * that they did indeed come from the OpenID service. */ public class TokenValidationService { /** - * Service for retrieving OAuth configuration information. + * Service for retrieving OpenID configuration information. */ @Inject private ConfigurationService confService; 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/openid/user/AuthenticatedUser.java similarity index 91% rename from extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/user/AuthenticatedUser.java rename to extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/user/AuthenticatedUser.java index 3a798eb4a..b7ff12549 100644 --- 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/openid/user/AuthenticatedUser.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.oauth.user; +package org.apache.guacamole.auth.openid.user; import com.google.inject.Inject; import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; @@ -25,8 +25,8 @@ 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 + * An openid-specific implementation of AuthenticatedUser, associating a + * username and particular set of credentials with the OpenID authentication * provider. */ public class AuthenticatedUser extends AbstractAuthenticatedUser { 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 add9607f1..3707a4f51 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" ] } diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js similarity index 79% rename from extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js rename to extensions/guacamole-auth-openid/src/main/resources/openidConfig.js index 431965697..455c66af2 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthConfig.js +++ b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js @@ -18,16 +18,16 @@ */ /** - * Config block which registers OAuth-specific field types. + * Config block which registers openid-specific field types. */ -angular.module('guacOAuth').config(['formServiceProvider', - function guacOAuthConfig(formServiceProvider) { +angular.module('guacOpenID').config(['formServiceProvider', + function guacOpenIDConfig(formServiceProvider) { - // Define field for token from OAuth service - formServiceProvider.registerFieldType("GUAC_OAUTH_TOKEN", { + // Define field for token from OpenID service + formServiceProvider.registerFieldType("GUAC_OPENID_TOKEN", { template : '', - controller : 'guacOAuthController', - module : 'guacOAuth' + controller : 'guacOpenIDController', + module : 'guacOpenID' }); }]); diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthController.js b/extensions/guacamole-auth-openid/src/main/resources/openidController.js similarity index 82% rename from extensions/guacamole-auth-openid/src/main/resources/oauthController.js rename to extensions/guacamole-auth-openid/src/main/resources/openidController.js index ba7a1208d..a1fad885c 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthController.js +++ b/extensions/guacamole-auth-openid/src/main/resources/openidController.js @@ -18,11 +18,11 @@ */ /** - * Controller for the "GUAC_OAUTH_TOKEN" field which simply redirects the user + * Controller for the "GUAC_OPENID_TOKEN" field which simply redirects the user * immediately to the authorization URI. */ -angular.module('guacOAuth').controller('guacOAuthController', ['$scope', - function guacOAuthController($scope) { +angular.module('guacOpenID').controller('guacOpenIDController', ['$scope', + function guacOpenIDController($scope) { // Redirect to authorization URI window.location = $scope.field.authorizationURI; diff --git a/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js b/extensions/guacamole-auth-openid/src/main/resources/openidModule.js similarity index 79% rename from extensions/guacamole-auth-openid/src/main/resources/oauthModule.js rename to extensions/guacamole-auth-openid/src/main/resources/openidModule.js index 545b6b79c..e8fce23e2 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/oauthModule.js +++ b/extensions/guacamole-auth-openid/src/main/resources/openidModule.js @@ -18,11 +18,11 @@ */ /** - * Module which provides handling for OAuth authentication. + * Module which provides handling for OpenID authentication. */ -angular.module('guacOAuth', [ +angular.module('guacOpenID', [ 'form' ]); -// Ensure the OAuth module is loaded along with the rest of the app -angular.module('index').requires.push('guacOAuth'); +// Ensure the OpenID module is loaded along with the rest of the app +angular.module('index').requires.push('guacOpenID'); From 82c6048d504965da90b719fa948a9ee5d99edcbd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 12:45:37 -0800 Subject: [PATCH 20/28] GUACAMOLE-210: Move OpenID configuration property definitions into ConfigurationService. --- .../openid/conf/ConfigurationService.java | 85 +++++++++++++- .../conf/OpenIDGuacamoleProperties.java | 108 ------------------ 2 files changed, 79 insertions(+), 114 deletions(-) delete mode 100644 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/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java index 650cf47f4..6f7e44be2 100644 --- 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 @@ -22,6 +22,7 @@ package org.apache.guacamole.auth.openid.conf; import com.google.inject.Inject; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.StringGuacamoleProperty; /** * Service for retrieving configuration information regarding the OpenID @@ -29,6 +30,78 @@ import org.apache.guacamole.environment.Environment; */ public class ConfigurationService { + /** + * The authorization endpoint (URI) of the OpenID service. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private 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. + */ + private static final StringGuacamoleProperty OPENID_REDIRECT_URI = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-redirect-uri"; } + + }; + /** * The Guacamole server environment. */ @@ -48,7 +121,7 @@ public class ConfigurationService { * endpoint property is missing. */ public String getAuthorizationEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_AUTHORIZATION_ENDPOINT); + return environment.getRequiredProperty(OPENID_AUTHORIZATION_ENDPOINT); } /** @@ -66,7 +139,7 @@ public class ConfigurationService { * property is missing. */ public String getClientID() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_CLIENT_ID); + return environment.getRequiredProperty(OPENID_CLIENT_ID); } /** @@ -84,7 +157,7 @@ public class ConfigurationService { * property is missing. */ public String getRedirectURI() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_REDIRECT_URI); + return environment.getRequiredProperty(OPENID_REDIRECT_URI); } /** @@ -100,7 +173,7 @@ public class ConfigurationService { * is missing. */ public String getIssuer() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_ISSUER); + return environment.getRequiredProperty(OPENID_ISSUER); } /** @@ -118,7 +191,7 @@ public class ConfigurationService { * property is missing. */ public String getJWKSEndpoint() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_JWKS_ENDPOINT); + return environment.getRequiredProperty(OPENID_JWKS_ENDPOINT); } /** @@ -134,7 +207,7 @@ public class ConfigurationService { * type property is missing. */ public String getUsernameClaimType() throws GuacamoleException { - return environment.getRequiredProperty(OpenIDGuacamoleProperties.OPENID_USERNAME_CLAIM_TYPE); + return environment.getRequiredProperty(OPENID_USERNAME_CLAIM_TYPE); } } 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 deleted file mode 100644 index 2049ccaf5..000000000 --- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/OpenIDGuacamoleProperties.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.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"; } - - }; - -} From b59c1e72335d8585ce32ac9351baf564c469372f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 13:28:52 -0800 Subject: [PATCH 21/28] GUACAMOLE-210: Use empty template if field otherwise has no template. --- .../src/main/webapp/app/form/services/formService.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js index c117bbf7f..64a171310 100644 --- a/guacamole/src/main/webapp/app/form/services/formService.js +++ b/guacamole/src/main/webapp/app/form/services/formService.js @@ -244,9 +244,16 @@ angular.module('form').provider('formService', function formServiceProvider() { } // If no raw HTML template is provided, retrieve template from URL - else + else if (fieldType.templateUrl) templateRequest = $templateRequest(fieldType.templateUrl); + // Otherwise, use empty template + else { + var emptyTemplate= $q.defer(); + emptyTemplate.resolve(''); + templateRequest = emptyTemplate.promise; + } + // Defer compilation of template pending successful retrieval var compiledTemplate = $q.defer(); From 28cfc39c11838d6972c21ef92d6f07daaad0214e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 Feb 2017 13:29:23 -0800 Subject: [PATCH 22/28] GUACAMOLE-210: Remove unnecessary empty template definitions (empty template is the default). --- .../guacamole-auth-openid/src/main/resources/openidConfig.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js index 455c66af2..cf4c81e5d 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js +++ b/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js @@ -25,7 +25,6 @@ angular.module('guacOpenID').config(['formServiceProvider', // Define field for token from OpenID service formServiceProvider.registerFieldType("GUAC_OPENID_TOKEN", { - template : '', controller : 'guacOpenIDController', module : 'guacOpenID' }); From 724a6a9737436b6e9a01eb209179e8ff34713758 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 26 Aug 2017 17:52:53 -0700 Subject: [PATCH 23/28] GUACAMOLE-210: Update API to 0.9.13-incubating. --- extensions/guacamole-auth-openid/pom.xml | 4 ++-- .../guacamole/auth/openid/OpenIDAuthenticationProvider.java | 5 +++++ .../src/main/resources/guac-manifest.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 89f5b6cbd..14aea2b04 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -26,7 +26,7 @@ org.apache.guacamole guacamole-auth-openid jar - 0.9.11-incubating + 0.9.13-incubating guacamole-auth-openid http://guacamole.incubator.apache.org/ @@ -130,7 +130,7 @@ org.apache.guacamole guacamole-ext - 0.9.11-incubating + 0.9.13-incubating provided 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 index 7fa854832..42da6df3d 100644 --- 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 @@ -63,6 +63,11 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider { return "openid"; } + @Override + public Object getResource() throws GuacamoleException { + return null; + } + @Override public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { 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 3707a4f51..c8d626f48 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -1,6 +1,6 @@ { - "guacamoleVersion" : "0.9.11-incubating", + "guacamoleVersion" : "0.9.13-incubating", "name" : "OpenID Authentication Extension", "namespace" : "guac-openid", From 187903563b3b9dd0a9721a5dafe5a4a58cb268c0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 27 Aug 2017 22:36:06 -0700 Subject: [PATCH 24/28] GUACAMOLE-210: Add redirect message. Refactor to use minification and pre-cached templates. --- extensions/guacamole-auth-openid/.gitignore | 3 + extensions/guacamole-auth-openid/pom.xml | 80 +++++++++++++++++++ .../resources/{ => config}/openidConfig.js | 5 +- .../{ => controllers}/openidController.js | 0 .../src/main/resources/guac-manifest.json | 18 ++++- .../src/main/resources/license.txt | 18 +++++ .../src/main/resources/styles/openid.css | 35 ++++++++ .../resources/templates/openidTokenField.html | 5 ++ .../src/main/resources/translations/en.json | 12 +++ 9 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 extensions/guacamole-auth-openid/.gitignore rename extensions/guacamole-auth-openid/src/main/resources/{ => config}/openidConfig.js (91%) rename extensions/guacamole-auth-openid/src/main/resources/{ => controllers}/openidController.js (100%) create mode 100644 extensions/guacamole-auth-openid/src/main/resources/license.txt create mode 100644 extensions/guacamole-auth-openid/src/main/resources/styles/openid.css create mode 100644 extensions/guacamole-auth-openid/src/main/resources/templates/openidTokenField.html create mode 100644 extensions/guacamole-auth-openid/src/main/resources/translations/en.json diff --git a/extensions/guacamole-auth-openid/.gitignore b/extensions/guacamole-auth-openid/.gitignore new file mode 100644 index 000000000..30eb48707 --- /dev/null +++ b/extensions/guacamole-auth-openid/.gitignore @@ -0,0 +1,3 @@ +*~ +target/ +src/main/resources/generated/ diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml index 14aea2b04..4076998f1 100644 --- a/extensions/guacamole-auth-openid/pom.xml +++ b/extensions/guacamole-auth-openid/pom.xml @@ -53,6 +53,85 @@ + + + com.keithbranton.mojo + angular-maven-plugin + 0.3.2 + + + generate-resources + + html2js + + + + + ${basedir}/src/main/resources + **/*.html + ${basedir}/src/main/resources/generated/templates-main/templates.js + app/ext/guac-openid + + + + + + com.samaxes.maven + minify-maven-plugin + 1.7.5 + + + default-cli + + UTF-8 + + ${basedir}/src/main/resources + ${project.build.directory}/classes + + / + / + openid.css + + + license.txt + + + + **/*.css + + + / + / + openid.js + + + license.txt + + + + **/*.js + + + + + **/*.test.js + + CLOSURE + + + + OFF + OFF + + + + + minify + + + + + org.apache.maven.plugins @@ -105,6 +184,7 @@ **/*.json src/licenses/**/* + src/main/resources/templates/*.html diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js b/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js similarity index 91% rename from extensions/guacamole-auth-openid/src/main/resources/openidConfig.js rename to extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js index cf4c81e5d..12bc0dabb 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/openidConfig.js +++ b/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js @@ -25,8 +25,9 @@ angular.module('guacOpenID').config(['formServiceProvider', // Define field for token from OpenID service formServiceProvider.registerFieldType("GUAC_OPENID_TOKEN", { - controller : 'guacOpenIDController', - module : 'guacOpenID' + templateUrl : 'app/ext/guac-openid/templates/openidTokenField.html', + controller : 'guacOpenIDController', + module : 'guacOpenID' }); }]); diff --git a/extensions/guacamole-auth-openid/src/main/resources/openidController.js b/extensions/guacamole-auth-openid/src/main/resources/controllers/openidController.js similarity index 100% rename from extensions/guacamole-auth-openid/src/main/resources/openidController.js rename to extensions/guacamole-auth-openid/src/main/resources/controllers/openidController.js 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 c8d626f48..e71c140fa 100644 --- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json +++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json @@ -9,10 +9,20 @@ "org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider" ], + "translations" : [ + "translations/en.json" + ], + "js" : [ - "openidModule.js", - "openidController.js", - "openidConfig.js" - ] + "openid.min.js" + ], + + "css" : [ + "openid.min.css" + ], + + "resources" : { + "templates/openidTokenField.html" : "text/html" + } } diff --git a/extensions/guacamole-auth-openid/src/main/resources/license.txt b/extensions/guacamole-auth-openid/src/main/resources/license.txt new file mode 100644 index 000000000..042f3ce1f --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/license.txt @@ -0,0 +1,18 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ diff --git a/extensions/guacamole-auth-openid/src/main/resources/styles/openid.css b/extensions/guacamole-auth-openid/src/main/resources/styles/openid.css new file mode 100644 index 000000000..eab7f935d --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/styles/openid.css @@ -0,0 +1,35 @@ +/* + * 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. + */ + +.openid-token-field-container { + height: 100%; + width: 100%; + position: fixed; + left: 0; + top: 0; + display: table; + background: white; +} + +.openid-token-field { + width: 100%; + display: table-cell; + vertical-align: middle; + text-align: center; +} diff --git a/extensions/guacamole-auth-openid/src/main/resources/templates/openidTokenField.html b/extensions/guacamole-auth-openid/src/main/resources/templates/openidTokenField.html new file mode 100644 index 000000000..49f6c6f0a --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/templates/openidTokenField.html @@ -0,0 +1,5 @@ +
+
+

{{ 'LOGIN.INFO_REDIRECT_PENDING' | translate }}

+
+
diff --git a/extensions/guacamole-auth-openid/src/main/resources/translations/en.json b/extensions/guacamole-auth-openid/src/main/resources/translations/en.json new file mode 100644 index 000000000..6bc52410e --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/resources/translations/en.json @@ -0,0 +1,12 @@ +{ + + "DATA_SOURCE_OPENID" : { + "NAME" : "OpenID SSO Backend" + }, + + "LOGIN" : { + "FIELD_HEADER_ID_TOKEN" : "", + "INFO_REDIRECT_PENDING" : "Please wait, redirecting to identity provider..." + } + +} From 4f8c853daa34d85b68e40c54b92a7f09e6eeac73 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 27 Aug 2017 22:58:12 -0700 Subject: [PATCH 25/28] GUACAMOLE-210: Re-request ID token if validation or username retrieval fails. --- .../openid/AuthenticationProviderService.java | 18 ++++---- .../openid/token/TokenValidationService.java | 41 +++++++++++++------ 2 files changed, 40 insertions(+), 19 deletions(-) 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 index 10dea3d43..1423b8dfd 100644 --- 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 @@ -82,19 +82,23 @@ public class AuthenticationProviderService { public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { - String token = null; + String username = null; - // Pull OpenID token from request if present + // Validate OpenID token in request, if present, and derive username HttpServletRequest request = credentials.getRequest(); - if (request != null) - token = request.getParameter(TokenField.PARAMETER_NAME); + if (request != null) { + String token = request.getParameter(TokenField.PARAMETER_NAME); + if (token != null) + username = tokenService.processUsername(token); + } - // If token provided, validate and produce authenticated user - if (token != null) { + // If the username was successfully retrieved from the token, produce + // authenticated user + if (username != null) { // Create corresponding authenticated user AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(tokenService.processUsername(token), credentials); + authenticatedUser.init(username, credentials); return authenticatedUser; } 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 index b1a8a28f7..3e1a58dbb 100644 --- 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 @@ -31,6 +31,8 @@ import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Service for validating ID tokens forwarded to us by the client, verifying @@ -38,6 +40,11 @@ import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver; */ public class TokenValidationService { + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(TokenValidationService.class); + /** * Service for retrieving OpenID configuration information. */ @@ -48,17 +55,17 @@ public class TokenValidationService { * 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. + * token is invalid, null is returned. * * @param token * The ID token to validate and parse. * * @return - * The username contained within the given ID token. + * The username contained within the given ID token, or null if the ID + * token is not valid or the username claim type is missing, * * @throws GuacamoleException - * If the ID token is not valid, the username claim type is missing, or - * guacamole.properties could not be parsed. + * If guacamole.properties could not be parsed. */ public String processUsername(String token) throws GuacamoleException { @@ -79,27 +86,37 @@ public class TokenValidationService { try { + String usernameClaim = confService.getUsernameClaimType(); + // 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"); + String username = claims.getStringClaimValue(usernameClaim); + if (username != null) + return username; - // Username successfully retrieved from the JWT - return username; + // Warn if username was not present in token, as it likely means + // the system is not set up correctly + logger.warn("Username claim \"{}\" missing from token. Perhaps the " + + "OpenID scope and/or username claim type are " + + "misconfigured?", usernameClaim); } - // Rethrow any failures to validate/parse the JWT + // Log any failures to validate/parse the JWT catch (InvalidJwtException e) { - throw new GuacamoleSecurityException("Invalid ID token.", e); + logger.info("Rejected invalid OpenID token: {}", e.getMessage()); + logger.debug("Invalid JWT received.", e); } catch (MalformedClaimException e) { - throw new GuacamoleServerException("Unable to parse JWT claims.", e); + logger.info("Rejected OpenID token with malformed claim: {}", e.getMessage()); + logger.debug("Malformed claim within received JWT.", e); } + // Could not retrieve username from JWT + return null; + } } From aaf1b796f3201916b9a5e8269cefd9b88df183bc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 27 Aug 2017 23:58:15 -0700 Subject: [PATCH 26/28] GUACAMOLE-210: Properly generate and validate nonces. --- .../openid/AuthenticationProviderService.java | 10 +- .../OpenIDAuthenticationProviderModule.java | 2 + .../auth/openid/form/TokenField.java | 44 ++---- .../auth/openid/token/NonceService.java | 135 ++++++++++++++++++ .../openid/token/TokenValidationService.java | 20 +++ 5 files changed, 180 insertions(+), 31 deletions(-) create mode 100644 extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/NonceService.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 index 1423b8dfd..46e8b022c 100644 --- 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 @@ -25,6 +25,7 @@ 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.NonceService; import org.apache.guacamole.auth.openid.token.TokenValidationService; import org.apache.guacamole.auth.openid.user.AuthenticatedUser; import org.apache.guacamole.GuacamoleException; @@ -52,6 +53,12 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; + /** + * Service for validating and generating unique nonce values. + */ + @Inject + private NonceService nonceService; + /** * Service for validating received ID tokens. */ @@ -112,7 +119,8 @@ public class AuthenticationProviderService { new TokenField( confService.getAuthorizationEndpoint(), confService.getClientID(), - confService.getRedirectURI() + confService.getRedirectURI(), + nonceService.generate(30000 /* FIXME: Calculate appropriate value based on configuration */) ) })) 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 index 9abd6667f..17510cbe5 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -74,6 +75,7 @@ public class OpenIDAuthenticationProviderModule extends AbstractModule { // Bind openid-specific services bind(ConfigurationService.class); + bind(NonceService.class); bind(TokenValidationService.class); } 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 index 3ef5d9404..3f7c4547d 100644 --- 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 @@ -20,15 +20,12 @@ 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. + * Field definition which represents the token returned by an OpenID Connect + * service. */ public class TokenField extends Field { @@ -44,29 +41,12 @@ public class TokenField extends Field { 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. + * 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 @@ -80,9 +60,13 @@ public class TokenField extends Field { * @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. */ public TokenField(String authorizationEndpoint, String clientID, - String redirectURI) { + String redirectURI, String nonce) { // Init base field properties super(PARAMETER_NAME, "GUAC_OPENID_TOKEN"); @@ -94,7 +78,7 @@ public class TokenField extends Field { + "&response_type=id_token" + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") - + "&nonce=" + generateNonce(); + + "&nonce=" + nonce; } // Java is required to provide UTF-8 support diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/NonceService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/NonceService.java new file mode 100644 index 000000000..778112a76 --- /dev/null +++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/NonceService.java @@ -0,0 +1,135 @@ +/* + * 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.Singleton; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Service for generating and validating single-use random tokens (nonces). + */ +@Singleton +public class NonceService { + + /** + * Cryptographically-secure random number generator for generating the + * required nonce. + */ + private final SecureRandom random = new SecureRandom(); + + /** + * Map of all generated nonces to their corresponding expiration timestamps. + * This Map must be periodically swept of expired nonces to avoid growing + * without bound. + */ + private final Map nonces = new ConcurrentHashMap(); + + /** + * The timestamp of the last expired nonce sweep. + */ + private long lastSweep = System.currentTimeMillis(); + + /** + * The minimum amount of time to wait between sweeping expired nonces from + * the Map. + */ + private static final long SWEEP_INTERVAL = 60000; + + /** + * Iterates through the entire Map of generated nonces, removing any nonce + * that has exceeded its expiration timestamp. If insufficient time has + * elapsed since the last sweep, as dictated by SWEEP_INTERVAL, this + * function has no effect. + */ + private void sweepExpiredNonces() { + + // Do not sweep until enough time has elapsed since the last sweep + long currentTime = System.currentTimeMillis(); + if (currentTime - lastSweep < SWEEP_INTERVAL) + return; + + // Record time of sweep + lastSweep = currentTime; + + // For each stored nonce + Iterator> entries = nonces.entrySet().iterator(); + while (entries.hasNext()) { + + // Remove all entries which have expired + Map.Entry current = entries.next(); + if (current.getValue() <= System.currentTimeMillis()) + entries.remove(); + + } + + } + + /** + * Generates a cryptographically-secure nonce value. The nonce is intended + * to be used to prevent replay attacks. + * + * @param maxAge + * The maximum amount of time that the generated nonce should remain + * valid, in milliseconds. + * + * @return + * A cryptographically-secure nonce value. + */ + public String generate(long maxAge) { + + // Sweep expired nonces if enough time has passed + sweepExpiredNonces(); + + // Generate and store nonce, along with expiration timestamp + String nonce = new BigInteger(130, random).toString(32); + nonces.put(nonce, System.currentTimeMillis() + maxAge); + return nonce; + + } + + /** + * Returns whether the give nonce value is valid. A nonce is valid if and + * only if it was generated by this instance of the NonceService. Testing + * nonce validity through this function immediately and permanently + * invalidates that nonce. + * + * @param nonce + * The nonce value to test. + * + * @return + * true if the provided nonce is valid, false otherwise. + */ + public boolean isValid(String nonce) { + + // Remove nonce, verifying whether it was present at all + Long expires = nonces.remove(nonce); + if (expires == null) + return false; + + // Nonce is only valid if it hasn't expired + return expires > System.currentTimeMillis(); + + } + +} 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 index 3e1a58dbb..3d41ebafe 100644 --- 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 @@ -51,6 +51,12 @@ public class TokenValidationService { @Inject private ConfigurationService confService; + /** + * Service for validating and generating unique nonce values. + */ + @Inject + private NonceService nonceService; + /** * Validates and parses the given ID token, returning the username contained * therein, as defined by the username claim type given in @@ -91,6 +97,20 @@ public class TokenValidationService { // Validate JWT JwtClaims claims = jwtConsumer.processToClaims(token); + // Verify a nonce is present + String nonce = claims.getStringClaimValue("nonce"); + if (nonce == null) { + logger.info("Rejected OpenID token without nonce."); + return null; + } + + // Verify that we actually generated the nonce, and that it has not + // already been used + if (!nonceService.isValid(nonce)) { + logger.debug("Rejected OpenID token with invalid/old nonce."); + return null; + } + // Pull username from claims String username = claims.getStringClaimValue(usernameClaim); if (username != null) From 4dbf9a3f9ed899ca614f74871c05b4cd901b6e73 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 28 Aug 2017 02:04:21 -0700 Subject: [PATCH 27/28] GUACAMOLE-210: Add configuration options for scope, clock skew, etc., as well as sensible defaults. --- .../openid/AuthenticationProviderService.java | 3 +- .../openid/conf/ConfigurationService.java | 157 +++++++++++++++++- .../auth/openid/form/TokenField.java | 10 +- .../openid/token/TokenValidationService.java | 6 +- 4 files changed, 164 insertions(+), 12 deletions(-) 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 index 46e8b022c..47d99fff6 100644 --- 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 @@ -118,9 +118,10 @@ public class AuthenticationProviderService { // to the authorization page via JavaScript) new TokenField( confService.getAuthorizationEndpoint(), + confService.getScope(), confService.getClientID(), confService.getRedirectURI(), - nonceService.generate(30000 /* FIXME: Calculate appropriate value based on configuration */) + nonceService.generate(confService.getMaxNonceValidity() * 60000L) ) })) 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 index 6f7e44be2..c742d8991 100644 --- 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 @@ -22,6 +22,7 @@ package org.apache.guacamole.auth.openid.conf; import com.google.inject.Inject; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; /** @@ -30,6 +31,35 @@ import org.apache.guacamole.properties.StringGuacamoleProperty; */ public class ConfigurationService { + /** + * The default claim type to use to retrieve an authenticated user's + * username. + */ + private static final String DEFAULT_USERNAME_CLAIM_TYPE = "email"; + + /** + * The default space-separated list of OpenID scopes to request. + */ + private static final String DEFAULT_SCOPE = "openid email profile"; + + /** + * The default amount of clock skew tolerated for timestamp comparisons + * between the Guacamole server and OpenID service clocks, in seconds. + */ + private static final int DEFAULT_ALLOWED_CLOCK_SKEW = 30; + + /** + * The default maximum amount of time that an OpenID token should remain + * valid, in minutes. + */ + private static final int DEFAULT_MAX_TOKEN_VALIDITY = 300; + + /** + * The default maximum amount of time that a nonce generated by the + * Guacamole server should remain valid, in minutes. + */ + private static final int DEFAULT_MAX_NONCE_VALIDITY = 10; + /** * The authorization endpoint (URI) of the OpenID service. */ @@ -76,6 +106,56 @@ public class ConfigurationService { }; + /** + * The space-separated list of OpenID scopes to request. + */ + private static final StringGuacamoleProperty OPENID_SCOPE = + new StringGuacamoleProperty() { + + @Override + public String getName() { return "openid-scope"; } + + }; + + /** + * The amount of clock skew tolerated for timestamp comparisons between the + * Guacamole server and OpenID service clocks, in seconds. + */ + private static final IntegerGuacamoleProperty OPENID_ALLOWED_CLOCK_SKEW = + new IntegerGuacamoleProperty() { + + @Override + public String getName() { return "openid-allowed-clock-skew"; } + + }; + + /** + * The maximum amount of time that an OpenID token should remain valid, in + * minutes. + */ + private static final IntegerGuacamoleProperty OPENID_MAX_TOKEN_VALIDITY = + new IntegerGuacamoleProperty() { + + @Override + public String getName() { return "openid-max-token-validity"; } + + }; + + /** + * The maximum amount of time that a nonce generated by the Guacamole server + * should remain valid, in minutes. As each OpenID request has a unique + * nonce value, this imposes an upper limit on the amount of time any + * particular OpenID request can result in successful authentication within + * Guacamole. + */ + private static final IntegerGuacamoleProperty OPENID_MAX_NONCE_VALIDITY = + new IntegerGuacamoleProperty() { + + @Override + public String getName() { return "openid-max-nonce-validity"; } + + }; + /** * OpenID client ID which should be submitted to the OpenID service when * necessary. This value is typically provided by the OpenID service when @@ -196,18 +276,87 @@ public class ConfigurationService { /** * Returns the claim type which contains the authenticated user's username - * within any valid JWT, as configured with guacamole.properties. + * within any valid JWT, as configured with guacamole.properties. By + * default, this will be "email". * * @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. + * If guacamole.properties cannot be parsed. */ public String getUsernameClaimType() throws GuacamoleException { - return environment.getRequiredProperty(OPENID_USERNAME_CLAIM_TYPE); + return environment.getProperty(OPENID_USERNAME_CLAIM_TYPE, DEFAULT_USERNAME_CLAIM_TYPE); + } + + /** + * Returns the space-separated list of OpenID scopes to request. By default, + * this will be "openid email profile". The OpenID scopes determine the + * information returned within the OpenID token, and thus affect what + * values can be used as an authenticated user's username. + * + * @return + * The space-separated list of OpenID scopes to request when identifying + * a user. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public String getScope() throws GuacamoleException { + return environment.getProperty(OPENID_SCOPE, DEFAULT_SCOPE); + } + + /** + * Returns the amount of clock skew tolerated for timestamp comparisons + * between the Guacamole server and OpenID service clocks, in seconds. Too + * much clock skew will affect token expiration calculations, possibly + * allowing old tokens to be used. By default, this will be 30. + * + * @return + * The amount of clock skew tolerated for timestamp comparisons, in + * seconds. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public int getAllowedClockSkew() throws GuacamoleException { + return environment.getProperty(OPENID_ALLOWED_CLOCK_SKEW, DEFAULT_ALLOWED_CLOCK_SKEW); + } + + /** + * Returns the maximum amount of time that an OpenID token should remain + * valid, in minutes. A token received from an OpenID service which is + * older than this amount of time will be rejected, even if it is otherwise + * valid. By default, this will be 300 (5 hours). + * + * @return + * The maximum amount of time that an OpenID token should remain valid, + * in minutes. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public int getMaxTokenValidity() throws GuacamoleException { + return environment.getProperty(OPENID_MAX_TOKEN_VALIDITY, DEFAULT_MAX_TOKEN_VALIDITY); + } + + /** + * Returns the maximum amount of time that a nonce generated by the + * Guacamole server should remain valid, in minutes. As each OpenID request + * has a unique nonce value, this imposes an upper limit on the amount of + * time any particular OpenID request can result in successful + * authentication within Guacamole. By default, this will be 10. + * + * @return + * The maximum amount of time that a nonce generated by the Guacamole + * server should remain valid, in minutes. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public int getMaxNonceValidity() throws GuacamoleException { + return environment.getProperty(OPENID_MAX_NONCE_VALIDITY, DEFAULT_MAX_NONCE_VALIDITY); } } 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 index 3f7c4547d..d99c3672d 100644 --- 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 @@ -52,6 +52,10 @@ public class TokenField extends Field { * 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 @@ -65,8 +69,8 @@ public class TokenField extends Field { * A random string unique to this request. To defend against replay * attacks, this value must cease being valid after its first use. */ - public TokenField(String authorizationEndpoint, String clientID, - String redirectURI, String nonce) { + public TokenField(String authorizationEndpoint, String scope, + String clientID, String redirectURI, String nonce) { // Init base field properties super(PARAMETER_NAME, "GUAC_OPENID_TOKEN"); @@ -74,7 +78,7 @@ public class TokenField extends Field { // Build authorization URI from given values try { this.authorizationURI = authorizationEndpoint - + "?scope=openid%20email%20profile" + + "?scope=" + URLEncoder.encode(scope, "UTF-8") + "&response_type=id_token" + "&client_id=" + URLEncoder.encode(clientID, "UTF-8") + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8") 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 index 3d41ebafe..cde4f89a6 100644 --- 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 @@ -22,8 +22,6 @@ 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; @@ -82,8 +80,8 @@ public class TokenValidationService { // Create JWT consumer for validating received token JwtConsumer jwtConsumer = new JwtConsumerBuilder() .setRequireExpirationTime() - .setMaxFutureValidityInMinutes(300) - .setAllowedClockSkewInSeconds(30) + .setMaxFutureValidityInMinutes(confService.getMaxTokenValidity()) + .setAllowedClockSkewInSeconds(confService.getAllowedClockSkew()) .setRequireSubject() .setExpectedIssuer(confService.getIssuer()) .setExpectedAudience(confService.getClientID()) From 4e459b9f19752559053bac6acd0f25d202a90df8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Sep 2017 13:09:11 -0700 Subject: [PATCH 28/28] GUACAMOLE-210: Implement AuthenticationProvider shutdown() function required due to GUACAMOLE-393. --- .../guacamole/auth/openid/OpenIDAuthenticationProvider.java | 5 +++++ 1 file changed, 5 insertions(+) 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 index 42da6df3d..57b483183 100644 --- 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 @@ -107,4 +107,9 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider { } + @Override + public void shutdown() { + // Do nothing + } + }