Skip to content

Commit db3342a

Browse files
committed
GUACAMOLE-2199: Adds support dynamic Base URL injection via configured header. Includes unit tests for API compatibility.
1 parent f7a9d87 commit db3342a

4 files changed

Lines changed: 143 additions & 3 deletions

File tree

guacamole/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,5 @@
378378
<artifactId>junit</artifactId>
379379
<scope>test</scope>
380380
</dependency>
381-
382381
</dependencies>
383-
384382
</project>

guacamole/src/main/java/org/apache/guacamole/rest/auth/APIAuthenticationResult.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ public class APIAuthenticationResult {
5252
*/
5353
private final List<String> availableDataSources;
5454

55+
/**
56+
* The baseUrl injected by the requester to be returned for load balancing purposes.
57+
*/
58+
private final String baseUrl;
59+
60+
61+
/**
62+
* Returns the baseUrl that should be used by the client for subsequent requests,
63+
* if any. This is typically used for load balancing or redirection.
64+
*
65+
* @return
66+
* The baseUrl to be used, or null if no specific baseUrl is required.
67+
*/
68+
public String getBaseUrl() {
69+
return baseUrl;
70+
}
71+
5572
/**
5673
* Returns the unique authentication token which identifies the current
5774
* session.
@@ -118,10 +135,39 @@ public List<String> getAvailableDataSources() {
118135
*/
119136
public APIAuthenticationResult(String authToken, String username,
120137
String dataSource, List<String> availableDataSources) {
138+
// Chain to the new constructor with null for baseUrl
139+
this(authToken, username, dataSource, availableDataSources, null);
140+
}
141+
142+
/**
143+
* Create a new APIAuthenticationResult object containing the given data.
144+
*
145+
* @param authToken
146+
* The unique token generated for the user that authenticated, to be
147+
* used for the duration of their session.
148+
*
149+
* @param username
150+
* The username of the user owning the given token.
151+
*
152+
* @param dataSource
153+
* The unique identifier of the AuthenticationProvider which
154+
* authenticated the user.
155+
*
156+
* @param availableDataSources
157+
* The unique identifier of all AuthenticationProviders to which the
158+
* user now has access.
159+
*
160+
* @param baseUrl
161+
* The base URL that the client should use for subsequent requests,
162+
* or null if the standard URL should be used.
163+
*/
164+
public APIAuthenticationResult(String authToken, String username,
165+
String dataSource, List<String> availableDataSources, String baseUrl) {
121166
this.authToken = authToken;
122167
this.username = username;
123168
this.dataSource = dataSource;
124169
this.availableDataSources = Collections.unmodifiableList(availableDataSources);
170+
this.baseUrl = baseUrl;
125171
}
126172

127173
}

guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
import javax.ws.rs.core.MultivaluedMap;
3737
import org.apache.guacamole.GuacamoleException;
3838
import org.apache.guacamole.GuacamoleResourceNotFoundException;
39+
import org.apache.guacamole.environment.Environment;
3940
import org.apache.guacamole.net.auth.AuthenticatedUser;
4041
import org.apache.guacamole.net.auth.Credentials;
4142
import org.apache.guacamole.net.auth.UserContext;
43+
import org.apache.guacamole.properties.StringGuacamoleProperty;
4244
import org.apache.guacamole.GuacamoleSession;
4345
import org.apache.guacamole.rest.APIRequest;
4446
import org.slf4j.Logger;
@@ -62,6 +64,25 @@ public class TokenRESTService {
6264
@Inject
6365
private AuthenticationService authenticationService;
6466

67+
/**
68+
* The Guacamole server environment.
69+
*/
70+
@Inject
71+
private Environment environment;
72+
73+
/**
74+
* The property within guacamole.properties which defines the HTTP header
75+
* that contains the client base URL (for service discovery/load balancing).
76+
*/
77+
private static final StringGuacamoleProperty AUTH_BASE_URL_HEADER = new StringGuacamoleProperty() {
78+
79+
@Override
80+
public String getName() {
81+
return "auth-base-url-header";
82+
}
83+
84+
};
85+
6586
/**
6687
* Returns the credentials associated with the given request, using the
6788
* provided username and password.
@@ -173,6 +194,19 @@ public APIAuthenticationResult createToken(@FormParam("username") String usernam
173194
// Create/update session producing possibly-new token
174195
token = authenticationService.authenticate(credentials, token);
175196

197+
// Determine if a custom Base URL should be injected
198+
String baseUrl = null;
199+
200+
// Use the injected environment instead of static access
201+
String headerName = environment.getProperty(AUTH_BASE_URL_HEADER);
202+
203+
if (headerName != null) {
204+
String headerValue = request.getHeader(headerName);
205+
if (headerValue != null) {
206+
baseUrl = headerValue.replace("{TOKEN}", token);
207+
}
208+
}
209+
176210
// Pull corresponding session
177211
GuacamoleSession session = authenticationService.getGuacamoleSession(token);
178212
if (session == null)
@@ -190,7 +224,8 @@ public APIAuthenticationResult createToken(@FormParam("username") String usernam
190224
token,
191225
authenticatedUser.getIdentifier(),
192226
authenticatedUser.getAuthenticationProvider().getIdentifier(),
193-
authProviderIdentifiers
227+
authProviderIdentifiers,
228+
baseUrl
194229
);
195230

196231
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.rest.auth;
21+
22+
import java.util.Collections;
23+
import org.junit.Test;
24+
import static org.junit.Assert.*;
25+
26+
public class APIAuthenticationResultTest {
27+
28+
/**
29+
* Verifies that the baseUrl is correctly stored and retrieved when
30+
* using the new constructor.
31+
*/
32+
@Test
33+
public void testBaseUrlInjection() {
34+
String expectedUrl = "https://lb.example.com";
35+
APIAuthenticationResult result = new APIAuthenticationResult(
36+
"token",
37+
"user",
38+
"source",
39+
Collections.emptyList(),
40+
expectedUrl
41+
);
42+
43+
assertEquals(expectedUrl, result.getBaseUrl());
44+
}
45+
46+
/**
47+
* Verifies that the legacy constructor still works and results in a
48+
* null baseUrl (backward compatibility).
49+
*/
50+
@Test
51+
public void testLegacyConstructor() {
52+
APIAuthenticationResult result = new APIAuthenticationResult(
53+
"token",
54+
"user",
55+
"source",
56+
Collections.emptyList()
57+
);
58+
59+
assertNull(result.getBaseUrl());
60+
}
61+
}

0 commit comments

Comments
 (0)