Skip to content

Commit ba50fd1

Browse files
author
David Bateman
committed
GUACAMOLE-2258: Allow redirection to original page after login
- Allow store the full URI in RequestDetails - Add methods to get/set originalURI in AuthenticatedUser and derived classes - Set the originalURI in JDBC base class - Promote session manager to base SSO class; Use it to store the originalURI in openid - Allow redirection to be stored in GuacamoleSession iff new login - Store the redirection in the GuacamoleSession the AuthenticationService - If redirection set in GuacamoleSession pass it back on the /api/tokens REST endpoint - In the javascript in the redirection is set on the /api/tokens endpoint, redirect after authentication
1 parent 7f3ba49 commit ba50fd1

14 files changed

Lines changed: 341 additions & 21 deletions

File tree

extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ public AuthenticatedUser authenticateUser(AuthenticationProvider authenticationP
8181

8282
// Authenticate user
8383
AuthenticatedUser user = userService.retrieveAuthenticatedUser(authenticationProvider, credentials);
84-
if (user != null)
84+
if (user != null) {
85+
user.setOriginalUri(credentials.getRequestDetails().getRequestURI());
8586
return user;
87+
}
8688

8789
// Otherwise, unauthorized
8890
throw new GuacamoleInvalidCredentialsException("Invalid login", CredentialsInfo.USERNAME_PASSWORD);

extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/RemoteAuthenticatedUser.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ public abstract class RemoteAuthenticatedUser implements AuthenticatedUser {
5151
*/
5252
private final Set<String> effectiveGroups;
5353

54+
/**
55+
* The URI that was originally used to the first call to the authentication
56+
* providers authenticateUser method.
57+
*/
58+
private String originalUri;
59+
5460
/**
5561
* Creates a new RemoteAuthenticatedUser, deriving the associated remote
5662
* host from the given credentials.
@@ -103,4 +109,14 @@ public void invalidate() {
103109
// Nothing to invalidate
104110
}
105111

112+
@Override
113+
public void setOriginalUri(String originalUri) {
114+
this.originalUri = originalUri;
115+
}
116+
117+
@Override
118+
public String getOriginalUri() {
119+
return this.originalUri;
120+
}
121+
106122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.guacamole.auth.sso.session;
21+
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.Map;
24+
import org.apache.guacamole.net.auth.AuthenticationSession;
25+
import org.apache.guacamole.net.auth.Credentials;
26+
27+
/**
28+
* Representation of an in-progress OpenID authentication attempt.
29+
*/
30+
public class SSOAuthenticationSession extends AuthenticationSession {
31+
/**
32+
* The key value used to store the redirection URI
33+
*/
34+
private static String REDIRECTION = "redirection";
35+
36+
/**
37+
* THe key value of the redirection URI in the credential parameers
38+
*/
39+
private static String REQUEST_HREF = "href";
40+
41+
/**
42+
* A Map of Arbitrary session data
43+
*/
44+
private final Map<String, Object> session;
45+
46+
/**
47+
* Creates a new AuthenticationSession representing an in-progress
48+
* authentication attempt.
49+
*
50+
* @param session
51+
* A Map of the session data to be stored
52+
*
53+
* @param expires
54+
* The number of milliseconds that may elapse before this session must
55+
* be considered invalid.
56+
*/
57+
public SSOAuthenticationSession(Map<String,Object> session, long expires) {
58+
super(expires);
59+
this.session = session;
60+
}
61+
62+
/**
63+
* Creates a new AuthenticationSession representing an in-progress
64+
* authentication attempt.
65+
*
66+
* @param expires
67+
* The number of milliseconds that may elapse before this session must
68+
* be considered invalid.
69+
*/
70+
public SSOAuthenticationSession(long expires) {
71+
this(new ConcurrentHashMap<>(), expires);
72+
}
73+
74+
/**
75+
* Returns the stored session data
76+
*
77+
* @return
78+
* The session data, can be null
79+
*/
80+
public Map<String, Object> getSession() {
81+
return session;
82+
}
83+
84+
/**
85+
* Returns an Object stored in the session data
86+
*
87+
* @return
88+
* The object in the session, can be null
89+
*/
90+
public Object get(String key) {
91+
return session.get(key);
92+
}
93+
94+
/**
95+
* Returns an Object stored in the session data
96+
*
97+
* @return
98+
* The object in the session, can be null
99+
*/
100+
public void put(String key, Object value) {
101+
session.put(key, value);
102+
}
103+
104+
/**
105+
* Special case for redirection from credentials to
106+
* simplify he authentication providers
107+
*
108+
* @return
109+
* The redirection stored in teh session
110+
*/
111+
public String getRedirection() {
112+
Object obj = session.get(REDIRECTION);
113+
return obj == null ? null : obj.toString();
114+
}
115+
116+
/**
117+
* Special case for redirection from credentials to
118+
* simplify he authentication providers
119+
*
120+
* @param credentials
121+
* The credentials from which to extract the redirection.
122+
*/
123+
public void setRedirection(Credentials credentials) {
124+
String redirection = credentials.getParameter(REQUEST_HREF);
125+
put(REDIRECTION, redirection);
126+
}
127+
}
128+
129+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.guacamole.auth.sso.session;
21+
22+
import java.util.Map;
23+
import com.google.inject.Singleton;
24+
import org.apache.guacamole.net.auth.AuthenticationSessionManager;
25+
26+
/**
27+
* Manager service that temporarily stores authentication attempts while
28+
* the authentication flow is underway.
29+
*/
30+
@Singleton
31+
public class SSOAuthenticationSessionManager
32+
extends AuthenticationSessionManager<SSOAuthenticationSession> {
33+
34+
/**
35+
* Returns the stored session data used with the identity provider
36+
*
37+
* @param identifier
38+
* The unique string returned by the call to defer(). For convenience,
39+
* this value may safely be null.
40+
*
41+
* @return
42+
* The session data
43+
*/
44+
public SSOAuthenticationSession resume(String identifier) {
45+
return super.resume(identifier);
46+
}
47+
}
48+

extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,29 @@
2424
import com.google.inject.Singleton;
2525
import java.net.URI;
2626
import java.util.Arrays;
27-
import java.util.Collection;
2827
import java.util.Collections;
2928
import java.util.Map;
3029
import java.util.Set;
3130
import javax.ws.rs.core.UriBuilder;
3231
import org.apache.guacamole.auth.openid.conf.ConfigurationService;
33-
import org.apache.guacamole.auth.openid.OpenIDAuthenticationSessionManager;
3432
import org.apache.guacamole.auth.openid.token.TokenValidationService;
3533
import org.apache.guacamole.auth.openid.util.PKCEUtil;
3634
import org.apache.guacamole.GuacamoleException;
3735
import org.apache.guacamole.auth.sso.NonceService;
3836
import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService;
37+
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSession;
38+
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSessionManager;
3939
import org.apache.guacamole.auth.sso.user.SSOAuthenticatedUser;
4040
import org.apache.guacamole.form.Field;
4141
import org.apache.guacamole.form.RedirectField;
4242
import org.apache.guacamole.language.TranslatableMessage;
4343
import org.apache.guacamole.net.auth.Credentials;
4444
import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
4545
import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
46-
import org.apache.guacamole.net.auth.IdentifierGenerator;
4746
import org.jose4j.jwt.JwtClaims;
4847
import org.slf4j.Logger;
4948
import org.slf4j.LoggerFactory;
5049

51-
5250
/**
5351
* Service that authenticates Guacamole users by processing OpenID tokens.
5452
*/
@@ -90,7 +88,7 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS
9088
* Manager of active OpenID authentication attempts.
9189
*/
9290
@Inject
93-
private OpenIDAuthenticationSessionManager sessionManager;
91+
private SSOAuthenticationSessionManager sessionManager;
9492

9593
/**
9694
* Service for validating and generating unique nonce values.
@@ -137,11 +135,16 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
137135
Set<String> groups = null;
138136
Map<String,String> tokens = Collections.emptyMap();
139137

140-
logger.debug("OpenID authentication with '{}' reponse type (ID: {}, Secret: {}, PKCE: {})",
138+
// Recover session
139+
String identifier = getSessionIdentifier(credentials);
140+
SSOAuthenticationSession session = sessionManager.resume(identifier);
141+
142+
logger.debug("OpenID authentication with '{}' reponse type (ID: {}, Secret: {}, PKCE: {}, Redirection: {})",
141143
confService.getResponseType(),
142144
confService.getClientID(),
143145
confService.getClientSecret(),
144-
confService.isPKCERequired());
146+
confService.isPKCERequired(),
147+
credentials.getParameter("href"));
145148

146149
if (confService.isImplicitFlow()) {
147150
String token = credentials.getParameter(IMPLICIT_TOKEN_PARAMETER_NAME);
@@ -157,10 +160,8 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
157160
else {
158161
String verifier = null;
159162
if (confService.isPKCERequired()) {
160-
// Recover session
161-
String identifier = getSessionIdentifier(credentials);
162-
if (identifier != null) {
163-
verifier = sessionManager.getVerifier(identifier);
163+
if (session != null) {
164+
verifier = session.get("verifier").toString();
164165
}
165166
}
166167
String code = credentials.getParameter("code");
@@ -180,14 +181,17 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
180181
// Create corresponding authenticated user
181182
SSOAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
182183
authenticatedUser.init(username, credentials, groups, tokens);
184+
if (session != null) {
185+
authenticatedUser.setOriginalUri(session.getRedirection());
186+
}
183187
return authenticatedUser;
184188
}
185189

186190
// Request OpenID token (will automatically redirect the user to the
187191
// OpenID authorization page via JavaScript)
188192
throw new GuacamoleInvalidCredentialsException("Invalid login.",
189193
new CredentialsInfo(Arrays.asList(new Field[] {
190-
new RedirectField(AUTH_SESSION_QUERY_PARAM, getLoginURI(),
194+
new RedirectField(AUTH_SESSION_QUERY_PARAM, getLoginURI(credentials),
191195
new TranslatableMessage("LOGIN.INFO_IDP_REDIRECT_PENDING"))
192196
}))
193197
);
@@ -196,6 +200,10 @@ public SSOAuthenticatedUser authenticateUser(Credentials credentials)
196200

197201
@Override
198202
public URI getLoginURI() throws GuacamoleException {
203+
return getLoginURI(null);
204+
}
205+
206+
private URI getLoginURI(Credentials credentials) throws GuacamoleException {
199207
UriBuilder builder = UriBuilder.fromUri(confService.getAuthorizationEndpoint())
200208
.queryParam("scope", confService.getScope())
201209
.queryParam("response_type", confService.getResponseType().toString())
@@ -215,10 +223,14 @@ public URI getLoginURI() throws GuacamoleException {
215223
}
216224

217225
// Store verifier for authenticateUser
218-
OpenIDAuthenticationSession session = new OpenIDAuthenticationSession(codeVerifier,
226+
SSOAuthenticationSession session = new SSOAuthenticationSession(
219227
confService.getMaxPKCEVerifierValidity() * 60000L);
220-
String identifier = IdentifierGenerator.generateIdentifier();
221-
sessionManager.defer(session, identifier);
228+
session.put("verifier", codeVerifier);
229+
if (credentials != null) {
230+
session.setRedirection(credentials);
231+
logger.debug("Redirection set : {}", session.getRedirection());
232+
}
233+
String identifier = sessionManager.defer(session);
222234

223235
builder.queryParam("code_challenge", codeChallenge)
224236
.queryParam("code_challenge_method", "S256")

extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/OpenIDAuthenticationProviderModule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.apache.guacamole.auth.openid.conf.ConfigurationService;
2626
import org.apache.guacamole.auth.openid.conf.OpenIDEnvironment;
2727
import org.apache.guacamole.auth.openid.conf.OpenIDWellKnown;
28-
import org.apache.guacamole.auth.openid.OpenIDAuthenticationSessionManager;
28+
import org.apache.guacamole.auth.sso.session.SSOAuthenticationSessionManager;
2929
import org.apache.guacamole.auth.sso.NonceService;
3030
import org.apache.guacamole.auth.openid.token.TokenValidationService;
3131
import org.apache.guacamole.environment.Environment;
@@ -45,7 +45,7 @@ protected void configure() {
4545
bind(ConfigurationService.class);
4646
bind(NonceService.class).in(Scopes.SINGLETON);
4747
bind(TokenValidationService.class);
48-
bind(OpenIDAuthenticationSessionManager.class);
48+
bind(SSOAuthenticationSessionManager.class).in(Scopes.SINGLETON);
4949

5050
bind(Environment.class).toInstance(environment);
5151
}

0 commit comments

Comments
 (0)