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"; } + + }; + +}