GUACAMOLE-210: POST code to OAuth service to retrieve token.

This commit is contained in:
Michael Jumper
2016-01-02 22:35:47 -08:00
parent c20271cb99
commit c3c6e0c43b
5 changed files with 294 additions and 113 deletions

View File

@@ -26,6 +26,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.guacamole.auth.oauth.user.AuthenticatedUser; import org.apache.guacamole.auth.oauth.user.AuthenticatedUser;
import org.apache.guacamole.auth.oauth.conf.ConfigurationService; import org.apache.guacamole.auth.oauth.conf.ConfigurationService;
import org.apache.guacamole.auth.oauth.form.OAuthCodeField; 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.GuacamoleException;
import org.glyptodon.guacamole.form.Field; import org.glyptodon.guacamole.form.Field;
import org.glyptodon.guacamole.net.auth.Credentials; import org.glyptodon.guacamole.net.auth.Credentials;
@@ -51,6 +53,12 @@ public class AuthenticationProviderService {
@Inject @Inject
private ConfigurationService confService; private ConfigurationService confService;
/**
* Service for producing authentication tokens from OAuth codes.
*/
@Inject
private TokenService tokenService;
/** /**
* Provider for AuthenticatedUser objects. * Provider for AuthenticatedUser objects.
*/ */
@@ -84,9 +92,16 @@ public class AuthenticationProviderService {
// TODO: Actually complete authentication using received code // TODO: Actually complete authentication using received code
if (code != null) { 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 authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init("STUB", credentials); authenticatedUser.init("STUB", credentials);
return authenticatedUser; return authenticatedUser;
} }
// Request auth code // Request auth code

View File

@@ -20,7 +20,13 @@
package org.apache.guacamole.auth.oauth; package org.apache.guacamole.auth.oauth;
import com.google.inject.AbstractModule; 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.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.GuacamoleException;
import org.glyptodon.guacamole.environment.Environment; import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment; import org.glyptodon.guacamole.environment.LocalEnvironment;
@@ -42,6 +48,12 @@ public class OAuthAuthenticationProviderModule extends AbstractModule {
*/ */
private final AuthenticationProvider authProvider; 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 * Creates a new OAuth authentication provider module which configures
* injection for the OAuthAuthenticationProvider. * injection for the OAuthAuthenticationProvider.
@@ -62,6 +74,15 @@ public class OAuthAuthenticationProviderModule extends AbstractModule {
// Store associated auth provider // Store associated auth provider
this.authProvider = authProvider; 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 @Override
@@ -73,6 +94,10 @@ public class OAuthAuthenticationProviderModule extends AbstractModule {
// Bind OAuth-specific services // Bind OAuth-specific services
bind(ConfigurationService.class); bind(ConfigurationService.class);
bind(TokenService.class);
// Bind HTTP client
bind(Client.class).toInstance(client);
} }

View File

@@ -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<AuthenticatedUser> authenticatedUserProvider;
/**
* Returns an AuthenticatedUser representing the user authenticated by the
* given credentials.
*
* @param credentials
* The credentials to use for authentication.
*
* @return
* An AuthenticatedUser representing the user authenticated by the
* given credentials.
*
* @throws GuacamoleException
* If an error occurs while authenticating the user, or if access is
* denied.
*/
public AuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
String 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()
)
}))
);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}