Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
*/
private final Set<String> effectiveGroups;

/**
* The URI that was originally used to the first call to the authentication
* providers authenticateUser method.
*/
private String originalUri;

/**
* Creates a new RemoteAuthenticatedUser, deriving the associated remote
* host from the given credentials.
Expand Down Expand Up @@ -103,4 +109,14 @@ public void invalidate() {
// Nothing to invalidate
}

@Override
public void setOriginalUri(String originalUri) {
this.originalUri = originalUri;
}

@Override
public String getOriginalUri() {
return this.originalUri;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.guacamole.auth.sso.session;

import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import org.apache.guacamole.net.auth.AuthenticationSession;
import org.apache.guacamole.net.auth.Credentials;

/**
* Representation of an in-progress OpenID authentication attempt.
*/
public class SSOAuthenticationSession extends AuthenticationSession {
/**
* The key value used to store the redirection URI
*/
private static String REDIRECTION = "redirection";

/**
* THe key value of the redirection URI in the credential parameers
*/
private static String REQUEST_HREF = "href";

/**
* A Map of Arbitrary session data
*/
private final Map<String, Object> session;

/**
* Creates a new AuthenticationSession representing an in-progress
* authentication attempt.
*
* @param session
* A Map of the session data to be stored
*
* @param expires
* The number of milliseconds that may elapse before this session must
* be considered invalid.
*/
public SSOAuthenticationSession(Map<String,Object> session, long expires) {
super(expires);
this.session = session;
}

/**
* Creates a new AuthenticationSession representing an in-progress
* authentication attempt.
*
* @param expires
* The number of milliseconds that may elapse before this session must
* be considered invalid.
*/
public SSOAuthenticationSession(long expires) {
this(new ConcurrentHashMap<>(), expires);
}

/**
* Returns the stored session data
*
* @return
* The session data, can be null
*/
public Map<String, Object> getSession() {
return session;
}

/**
* Returns an Object stored in the session data
*
* @return
* The object in the session, can be null
*/
public Object get(String key) {
return session.get(key);
}

/**
* Returns an Object stored in the session data
*
* @return
* The object in the session, can be null
*/
public void put(String key, Object value) {
session.put(key, value);
}

/**
* Special case for redirection from credentials to
* simplify he authentication providers
*
* @return
* The redirection stored in teh session
*/
public String getRedirection() {
Object obj = session.get(REDIRECTION);
return obj == null ? null : obj.toString();
}

/**
* Special case for redirection from credentials to
* simplify he authentication providers
*
* @param credentials
* The credentials from which to extract the redirection.
*/
public void setRedirection(Credentials credentials) {
String redirection = credentials.getParameter(REQUEST_HREF);
put(REDIRECTION, redirection);
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.guacamole.auth.sso.session;

import java.util.Map;
import com.google.inject.Singleton;
import org.apache.guacamole.net.auth.AuthenticationSessionManager;

/**
* Manager service that temporarily stores authentication attempts while
* the authentication flow is underway.
*/
@Singleton
public class SSOAuthenticationSessionManager
extends AuthenticationSessionManager<SSOAuthenticationSession> {

/**
* Returns the stored session data used with the identity provider
*
* @param identifier
* The unique string returned by the call to defer(). For convenience,
* this value may safely be null.
*
* @return
* The session data
*/
public SSOAuthenticationSession resume(String identifier) {
return super.resume(identifier);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.sso.NonceService;
import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSession;
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSessionManager;
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.RedirectField;
Expand Down Expand Up @@ -66,6 +68,12 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
@Inject
private NonceService nonceService;

/**
* Manager of active OpenID authentication attempts. Tracks redirection
*/
@Inject
private SSOAuthenticationSessionManager sessionManager;

/**
* Service for validating received ID tokens.
*/
Expand All @@ -78,6 +86,25 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
@Inject
private Provider<SSOAuthenticatedUser> authenticatedUserProvider;

/**
* Return the value of the session identifier associated with the given
* credentials, or null if no session identifier is found in the
* credentials.
*
* @param credentials
* The credentials from which to extract the session identifier.
*
* @return
* The session identifier associated with the given credentials, or
* null if no identifier is found.
*/
private static String getSessionIdentifier(Credentials credentials) {

// Return the session identifier from the request params, if set, or
// null otherwise
return credentials != null ? credentials.getParameter("state") : null;
}

@Override
public SSOAuthenticatedUser authenticateUser(Credentials credentials)
throws GuacamoleException {
Expand All @@ -86,6 +113,10 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
Set<String> groups = null;
Map<String,String> tokens = Collections.emptyMap();

// Recover session
String identifier = getSessionIdentifier(credentials);
SSOAuthenticationSession session = sessionManager.resume(identifier);

// Validate OpenID token in request, if present, and derive username
String token = credentials.getParameter(TOKEN_PARAMETER_NAME);
if (token != null) {
Expand All @@ -104,6 +135,9 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
// Create corresponding authenticated user
SSOAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
authenticatedUser.init(username, credentials, groups, tokens);
if (session != null) {
authenticatedUser.setOriginalUri(session.getRedirection());
}
return authenticatedUser;

}
Expand All @@ -112,7 +146,7 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
// OpenID authorization page via JavaScript)
throw new GuacamoleInvalidCredentialsException("Invalid login.",
new CredentialsInfo(Arrays.asList(new Field[] {
new RedirectField(TOKEN_PARAMETER_NAME, getLoginURI(),
new RedirectField(TOKEN_PARAMETER_NAME, getLoginURI(credentials),
new TranslatableMessage("LOGIN.INFO_IDP_REDIRECT_PENDING"))
}))
);
Expand All @@ -121,12 +155,24 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)

@Override
public URI getLoginURI() throws GuacamoleException {
return getLoginURI(null);
}

private URI getLoginURI(Credentials credentials) throws GuacamoleException {
// Create a session to store redirection, validity 10 miinutes
SSOAuthenticationSession session = new SSOAuthenticationSession(600000L);
if (credentials != null) {
session.setRedirection(credentials);
}
String identifier = sessionManager.defer(session);

return UriBuilder.fromUri(confService.getAuthorizationEndpoint())
.queryParam("scope", confService.getScope())
.queryParam("response_type", "id_token")
.queryParam("client_id", confService.getClientID())
.queryParam("redirect_uri", confService.getRedirectURI())
.queryParam("nonce", nonceService.generate(confService.getMaxNonceValidity() * 60000L))
.queryParam("state", identifier)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.guacamole.auth.openid.conf.ConfigurationService;
import org.apache.guacamole.auth.openid.conf.OpenIDEnvironment;
import org.apache.guacamole.auth.sso.NonceService;
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSessionManager;
import org.apache.guacamole.auth.openid.token.TokenValidationService;
import org.apache.guacamole.environment.Environment;

Expand All @@ -42,6 +43,7 @@ protected void configure() {
bind(ConfigurationService.class);
bind(NonceService.class).in(Scopes.SINGLETON);
bind(TokenValidationService.class);
bind(SSOAuthenticationSessionManager.class).in(Scopes.SINGLETON);

bind(Environment.class).toInstance(environment);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public class RequestDetails {
*/
private final List<Cookie> cookies;

/**
* The request URI of the associated request
*/
private final String requestURI;

/**
* Returns an unmodifiable Map of all HTTP headers within the given request.
* If there are no such headers, the returned Map will be empty. As there
Expand Down Expand Up @@ -193,6 +198,7 @@ public RequestDetails(HttpServletRequest request) {
this.parameters = getParameters(request);
this.remoteAddress = request.getRemoteAddr();
this.remoteHostname = request.getRemoteHost();
this.requestURI = request.getRequestURI();
this.session = request.getSession(false);
}

Expand All @@ -209,6 +215,7 @@ public RequestDetails(RequestDetails requestDetails) {
this.parameters = requestDetails.getParameters();
this.remoteAddress = requestDetails.getRemoteAddress();
this.remoteHostname = requestDetails.getRemoteHostname();
this.requestURI = requestDetails.getRequestURI();
this.session = requestDetails.getSession();
}

Expand Down Expand Up @@ -412,4 +419,14 @@ public String getRemoteHostname() {
return remoteHostname;
}

/**
* Returns the request URI used by the client that sent the associated request
*
* @return
* The original request URI
*/
public String getRequestURI() {
return requestURI;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
public abstract class AbstractAuthenticatedUser extends AbstractIdentifiable
implements AuthenticatedUser {

/**
* The URI that was originally used to the first call to the authentication
* providers authenticateUser method.
*/
private String originalUri;

/**
* Creates a new AbstractAuthenticatedUser that considers usernames to be
* case-sensitive or case-insensitive based on the provided case sensitivity
Expand Down Expand Up @@ -80,4 +86,14 @@ public void invalidate() {
// Nothing to invalidate
}

@Override
public void setOriginalUri(String originalUri) {
this.originalUri = originalUri;
}

@Override
public String getOriginalUri() {
return this.originalUri;
}

}
Loading
Loading