diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java index ff3555ca5..580dbf935 100644 --- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java +++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/DuoAuthenticationProvider.java @@ -21,7 +21,6 @@ package org.apache.guacamole.auth.duo; import com.google.inject.Guice; import com.google.inject.Injector; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; import org.apache.guacamole.net.auth.AuthenticatedUser; @@ -86,8 +85,7 @@ public class DuoAuthenticationProvider extends AbstractAuthenticationProvider { // Ignore requests with no corresponding authentication session ID, as // there are no credentials to reconstitute if the user has not yet // attempted to authenticate - HttpServletRequest request = credentials.getRequest(); - String duoState = request.getParameter(UserVerificationService.DUO_STATE_PARAMETER_NAME); + String duoState = credentials.getParameter(UserVerificationService.DUO_STATE_PARAMETER_NAME); if (duoState == null) return credentials; @@ -99,7 +97,7 @@ public class DuoAuthenticationProvider extends AbstractAuthenticationProvider { // Reconstitute the originally-provided credentials from the users // authentication attempt prior to being redirected to Duo Credentials previousCredentials = session.getCredentials(); - previousCredentials.setRequest(request); + previousCredentials.setRequestDetails(credentials.getRequestDetails()); return previousCredentials; } diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java index d2a6fa74d..557427df5 100644 --- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java +++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/UserVerificationService.java @@ -30,7 +30,6 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.concurrent.TimeUnit; import java.util.List; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.duo.conf.ConfigurationService; @@ -113,8 +112,7 @@ public class UserVerificationService { // Pull the original HTTP request used to authenticate Credentials credentials = authenticatedUser.getCredentials(); - HttpServletRequest request = credentials.getRequest(); - IPAddress clientAddr = new IPAddressString(request.getRemoteAddr()).getAddress(); + IPAddress clientAddr = new IPAddressString(credentials.getRemoteAddress()).getAddress(); // Ignore anonymous users String username = authenticatedUser.getIdentifier(); @@ -176,8 +174,8 @@ public class UserVerificationService { // Retrieve signed Duo authentication code and session state from the // request (these will be absent if this is an initial authentication // attempt and not a redirect back from Duo) - String duoCode = request.getParameter(DUO_CODE_PARAMETER_NAME); - String duoState = request.getParameter(DUO_STATE_PARAMETER_NAME); + String duoCode = credentials.getParameter(DUO_CODE_PARAMETER_NAME); + String duoState = credentials.getParameter(DUO_STATE_PARAMETER_NAME); // Redirect to Duo to obtain an authentication code if that redirect // has not yet occurred diff --git a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/AuthenticationProviderService.java b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/AuthenticationProviderService.java index c343be4c5..48bb18f96 100644 --- a/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-header/src/main/java/org/apache/guacamole/auth/header/AuthenticationProviderService.java @@ -21,13 +21,11 @@ package org.apache.guacamole.auth.header; import com.google.inject.Inject; import com.google.inject.Provider; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; import org.apache.guacamole.auth.header.user.AuthenticatedUser; -import java.security.Principal; /** * Service providing convenience functions for the HTTP Header @@ -65,19 +63,12 @@ public class AuthenticationProviderService { public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { - // Pull HTTP header from request if present - HttpServletRequest request = credentials.getRequest(); - if (request != null) { - - // Get the username from the header configured in guacamole.properties - String username = request.getHeader(confService.getHttpAuthHeader()); - - if (username != null) { - AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(username, credentials); - return authenticatedUser; - } - + // Get the username from the header configured in guacamole.properties + String username = credentials.getHeader(confService.getHttpAuthHeader()); + if (username != null) { + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init(username, credentials); + return authenticatedUser; } // Authentication not provided via header, yet, so we request it. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java index fe038b6cb..2ed0a950d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java @@ -22,7 +22,6 @@ package org.apache.guacamole.auth.jdbc.sharing; import com.google.inject.Inject; import java.util.Collections; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; @@ -175,13 +174,8 @@ public class ConnectionSharingService { */ public String getShareKey(Credentials credentials) { - // Pull associated HTTP request - HttpServletRequest request = credentials.getRequest(); - if (request == null) - return null; - // Retrieve the share key from the request - return request.getParameter(SHARE_KEY_NAME); + return credentials.getParameter(SHARE_KEY_NAME); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java index c85d78bc7..3f910c611 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java @@ -26,7 +26,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleUnsupportedException; @@ -522,9 +521,8 @@ public class UserService extends ModeledDirectoryObjectService trustedNetworks; @@ -79,14 +78,14 @@ public class RequestValidationService { // Deny all requests if restrictions cannot be parsed catch (GuacamoleException e) { - logger.warn("Authentication request from \"{}\" is DENIED due to parse error: {}", request.getRemoteAddr(), e.getMessage()); + logger.warn("Authentication request from \"{}\" is DENIED due to parse error: {}", credentials.getRemoteAddress(), e.getMessage()); logger.debug("Error parsing authentication request restrictions from guacamole.properties.", e); return false; } // All requests are allowed if no restrictions are defined if (trustedNetworks.isEmpty()) { - logger.debug("Authentication request from \"{}\" is ALLOWED (no restrictions).", request.getRemoteAddr()); + logger.debug("Authentication request from \"{}\" is ALLOWED (no restrictions).", credentials.getRemoteAddress()); return true; } @@ -94,15 +93,15 @@ public class RequestValidationService { for (String network : trustedNetworks) { // Request is allowed if any subnet matches - if (new IPAddressString(network).contains(new IPAddressString(request.getRemoteAddr()))) { - logger.debug("Authentication request from \"{}\" is ALLOWED (matched subnet).", request.getRemoteAddr()); + if (new IPAddressString(network).contains(new IPAddressString(credentials.getRemoteAddress()))) { + logger.debug("Authentication request from \"{}\" is ALLOWED (matched subnet).", credentials.getRemoteAddress()); return true; } } // Otherwise request is denied - no subnets matched - logger.debug("Authentication request from \"{}\" is DENIED (did not match subnet).", request.getRemoteAddr()); + logger.debug("Authentication request from \"{}\" is DENIED (did not match subnet).", credentials.getRemoteAddress()); return false; } diff --git a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/UserDataService.java b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/UserDataService.java index e3fee11e3..33b5beb7d 100644 --- a/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/UserDataService.java +++ b/extensions/guacamole-auth-json/src/main/java/org/apache/guacamole/auth/json/user/UserDataService.java @@ -31,7 +31,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.json.ConfigurationService; import org.apache.guacamole.auth.json.CryptoService; @@ -121,18 +120,13 @@ public class UserDataService { String json; byte[] correctSignature; - // Pull HTTP request, if available - HttpServletRequest request = credentials.getRequest(); - if (request == null) - return null; - // Abort if the request itself is not allowed - if (!requestService.isAuthenticationAllowed(request)) + if (!requestService.isAuthenticationAllowed(credentials)) return null; // Pull base64-encoded, encrypted JSON data from HTTP request, if any // such data is present - String base64 = request.getParameter(ENCRYPTED_DATA_PARAMETER); + String base64 = credentials.getParameter(ENCRYPTED_DATA_PARAMETER); if (base64 == null) return null; diff --git a/extensions/guacamole-auth-json/src/test/java/org/apache/guacamole/auth/json/RequestValidationServiceTest.java b/extensions/guacamole-auth-json/src/test/java/org/apache/guacamole/auth/json/RequestValidationServiceTest.java index ed5d77ce3..733b02d94 100644 --- a/extensions/guacamole-auth-json/src/test/java/org/apache/guacamole/auth/json/RequestValidationServiceTest.java +++ b/extensions/guacamole-auth-json/src/test/java/org/apache/guacamole/auth/json/RequestValidationServiceTest.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.ServletInputStream; import javax.servlet.RequestDispatcher; +import org.apache.guacamole.net.auth.Credentials; import org.junit.Test; import static org.junit.Assert.*; @@ -375,13 +376,13 @@ public class RequestValidationServiceTest { requestService = new RequestValidationService(new MockConfigurationService(null)); try { - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("1.1.1.1"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("10.10.10.10"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("100.100.100.100"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("1:1:1:1:1:1:1:1"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("10:10:10:10:10:10:10:10"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("100:100:100:100:100:100:100:100"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("1000:1000:1000:1000:1000:1000:1000:1000"))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1.1.1.1")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("10.10.10.10")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("100.100.100.100")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1:1:1:1:1:1:1:1")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("10:10:10:10:10:10:10:10")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("100:100:100:100:100:100:100:100")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1000:1000:1000:1000:1000:1000:1000:1000")))); } catch (AssertionError e) { fail("A network was denied to authenticate even though no trusted networks were specified."); @@ -399,18 +400,18 @@ public class RequestValidationServiceTest { requestService = new RequestValidationService(new MockConfigurationService("10.0.0.0/8,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,1.2.3.4/32,::1/128,fc00::/7")); try { - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("10.0.0.0"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("10.255.255.255"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("127.0.0.0"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("127.255.255.255"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("172.16.0.0"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("172.31.255.255"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("192.168.0.0"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("192.168.255.255"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("1.2.3.4"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("::1"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("fc00::"))); - assertTrue(requestService.isAuthenticationAllowed(mockHttpServletRequest("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("10.0.0.0")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("10.255.255.255")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("127.0.0.0")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("127.255.255.255")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("172.16.0.0")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("172.31.255.255")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("192.168.0.0")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("192.168.255.255")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1.2.3.4")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("::1")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("fc00::")))); + assertTrue(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))); } catch (AssertionError e) { fail("A trusted network was denied to authenticate."); @@ -428,20 +429,20 @@ public class RequestValidationServiceTest { requestService = new RequestValidationService(new MockConfigurationService("10.0.0.0/8,127.0.0.0/8,172.16.0.0/12,192.168.0.0/16,1.2.3.4/32,::1/128,fc00::/7")); try { - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("9.255.255.255"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("11.0.0.0"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("126.255.255.255"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("128.0.0.0"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("172.15.255.255"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("172.32.0.0"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("192.167.255.255"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("192.169.0.0"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("1.2.3.3"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("1.2.3.5"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("::0"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("::2"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); - assertFalse(requestService.isAuthenticationAllowed(mockHttpServletRequest("fe00::"))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("9.255.255.255")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("11.0.0.0")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("126.255.255.255")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("128.0.0.0")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("172.15.255.255")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("172.32.0.0")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("192.167.255.255")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("192.169.0.0")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1.2.3.3")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("1.2.3.5")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("::0")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("::2")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))); + assertFalse(requestService.isAuthenticationAllowed(new Credentials(null, null, mockHttpServletRequest("fe00::")))); } catch (AssertionError e) { fail("An untrusted network was allowed to authenticate."); diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java index e5e4f5797..8568ced7e 100644 --- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java @@ -23,7 +23,6 @@ import com.google.common.io.BaseEncoding; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Arrays; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.radius.user.AuthenticatedUser; import org.apache.guacamole.auth.radius.form.GuacamoleRadiusChallenge; import org.apache.guacamole.auth.radius.form.RadiusStateField; @@ -148,8 +147,7 @@ public class AuthenticationProviderService { return null; // Grab HTTP request object and a response to a challenge. - HttpServletRequest request = credentials.getRequest(); - String challengeResponse = request.getParameter(CHALLENGE_RESPONSE_PARAM); + String challengeResponse = credentials.getParameter(CHALLENGE_RESPONSE_PARAM); // RadiusPacket object to store response from server. RadiusPacket radPack; @@ -173,7 +171,7 @@ public class AuthenticationProviderService { // This is a response to a previous challenge, authenticate with that. else { try { - String stateString = request.getParameter(RadiusStateField.PARAMETER_NAME); + String stateString = credentials.getParameter(RadiusStateField.PARAMETER_NAME); if (stateString == null) { logger.warn("Expected state parameter was not present in challenge/response."); throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD); diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index f1e393d96..0980667a9 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -23,7 +23,6 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import java.net.URI; import java.util.Arrays; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.UriBuilder; import org.apache.guacamole.form.Field; import org.apache.guacamole.GuacamoleException; @@ -70,12 +69,9 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS throws GuacamoleException { // Pull CAS ticket from request if present - HttpServletRequest request = credentials.getRequest(); - if (request != null) { - String ticket = request.getParameter(TICKET_PARAMETER_NAME); - if (ticket != null) { - return ticketService.validateTicket(ticket, credentials); - } + String ticket = credentials.getParameter(TICKET_PARAMETER_NAME); + if (ticket != null) { + return ticketService.validateTicket(ticket, credentials); } // Request CAS ticket (will automatically redirect the user to the diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java index 17301f5b6..ca1be9e1f 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-openid/src/main/java/org/apache/guacamole/auth/openid/AuthenticationProviderService.java @@ -27,7 +27,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.UriBuilder; import org.apache.guacamole.auth.openid.conf.ConfigurationService; import org.apache.guacamole.auth.openid.token.TokenValidationService; @@ -88,16 +87,13 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS Map tokens = Collections.emptyMap(); // Validate OpenID token in request, if present, and derive username - HttpServletRequest request = credentials.getRequest(); - if (request != null) { - String token = request.getParameter(TOKEN_PARAMETER_NAME); - if (token != null) { - JwtClaims claims = tokenService.validateToken(token); - if (claims != null) { - username = tokenService.processUsername(claims); - groups = tokenService.processGroups(claims); - tokens = tokenService.processAttributes(claims); - } + String token = credentials.getParameter(TOKEN_PARAMETER_NAME); + if (token != null) { + JwtClaims claims = tokenService.validateToken(token); + if (claims != null) { + username = tokenService.processUsername(claims); + groups = tokenService.processGroups(claims); + tokens = tokenService.processAttributes(claims); } } diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java index 2c7990c4f..9d3bcc9f3 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java @@ -24,7 +24,6 @@ import com.google.inject.Provider; import com.google.inject.Singleton; import java.net.URI; import java.util.Arrays; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.saml.user.SAMLAuthenticatedUser; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.saml.acs.AssertedIdentity; @@ -85,21 +84,13 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS // Return the session identifier from the request params, if set, or // null otherwise - return credentials != null && credentials.getRequest() != null - ? credentials.getRequest().getParameter(AUTH_SESSION_QUERY_PARAM) - : null; + return credentials != null ? credentials.getParameter(AUTH_SESSION_QUERY_PARAM) : null; } @Override public SAMLAuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { - // No authentication can be attempted without a corresponding HTTP - // request - HttpServletRequest request = credentials.getRequest(); - if (request == null) - return null; - // Use established SAML identity if already provided by the SAML IdP AssertedIdentity identity = sessionManager.getIdentity( getSessionIdentifier(credentials)); diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationProviderService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationProviderService.java index 49d8daaff..8b8328f8b 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/AuthenticationProviderService.java @@ -25,7 +25,6 @@ import com.google.inject.Singleton; import java.net.URI; import java.util.Arrays; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.auth.ssl.conf.ConfigurationService; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleResourceNotFoundException; @@ -89,9 +88,7 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS // Return the session identifier from the request params, if set, or // null otherwise - return credentials != null && credentials.getRequest() != null - ? credentials.getRequest().getParameter(AUTH_SESSION_PARAMETER_NAME) - : null; + return credentials != null ? credentials.getParameter(AUTH_SESSION_PARAMETER_NAME) : null; } /** @@ -154,15 +151,9 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS // was signed by the expected CA. // - // We can't authenticate using SSL/TLS client auth unless there's an - // associated HTTP request - HttpServletRequest request = credentials.getRequest(); - if (request == null) - return null; - // We MUST have the domain associated with the request to ensure we // always get fresh SSL sessions when validating client certificates - String host = request.getHeader("Host"); + String host = credentials.getHeader("Host"); if (host == null) return null; diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java index e08042551..3ebd85b5b 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/user/UserVerificationService.java @@ -30,7 +30,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleUnsupportedException; @@ -317,10 +316,9 @@ public class UserVerificationService { // Pull the original HTTP request used to authenticate Credentials credentials = authenticatedUser.getCredentials(); - HttpServletRequest request = credentials.getRequest(); // Get the current client address - IPAddress clientAddr = new IPAddressString(request.getRemoteAddr()).getAddress(); + IPAddress clientAddr = new IPAddressString(credentials.getRemoteAddress()).getAddress(); // Ignore anonymous users if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER)) @@ -369,7 +367,7 @@ public class UserVerificationService { return; // Retrieve TOTP from request - String code = request.getParameter(AuthenticationCodeField.PARAMETER_NAME); + String code = credentials.getParameter(AuthenticationCodeField.PARAMETER_NAME); // If no TOTP provided, request one if (code == null) { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/RequestDetails.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/RequestDetails.java new file mode 100644 index 000000000..ca55ac9c8 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/RequestDetails.java @@ -0,0 +1,411 @@ +/* + * 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.net; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +/** + * A copy of the details of an HTTP request. The values within this object can + * be accessed even when outside the scope of the specific request represented. + */ +public class RequestDetails { + + /** + * The address of the client that sent the associated request. + */ + private final String remoteAddress; + + /** + * The hostname or, if the hostname cannot be determined, the address of the + * client that sent the associated request. + */ + private final String remoteHostname; + + /** + * The HttpSession associated with the request, if any. + */ + private final HttpSession session; + + /** + * An unmodifiable Map of all HTTP headers included in the associated + * request. If there are no such headers, this Map will be empty. As there + * may be many values for each header, each value is stored as a separate + * entry in an unmodifiable List. The keys of this map (header names) are + * case-insensitive. + */ + private final Map> headers; + + /** + * An unmodifiable Map of all HTTP parameters included in the associated + * request. If there are no such parameters, this Map will be empty. As + * there may be many values for each parameter, each value is stored as a + * separate entry in an unmodifiable List. Unlike headers, the keys of this + * map (parameter names) are case-sensitive. + */ + private final Map> parameters; + + /** + * An unmodifiable list of all cookies associated with the request. If + * there are no cookies associated with the request, this List will be + * empty. + */ + private final List cookies; + + /** + * 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 + * may be many values for each header, each value is stored as a separate + * entry in an unmodifiable List. The keys of the returned map (header + * names) are case-insensitive. + * + * @param request + * The HTTP request to extract all headers from. + * + * @return + * An unmodifiable Map of all HTTP headers in the given request. + */ + private static Map> getHeaders(HttpServletRequest request) { + + @SuppressWarnings("unchecked") // getHeaderNames() is explicitly documented as returning a Enumeration + Enumeration names = (Enumeration) request.getHeaderNames(); + + // Represent the total lack of headers as an empty map, not null + if (names == null) + return Collections.emptyMap(); + + // Headers are case-insensitive + Map> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + names.asIterator().forEachRemaining((String name) -> { + + @SuppressWarnings("unchecked") // getHeaders() is explicitly documented as returning a Enumeration + Enumeration values = (Enumeration) request.getHeaders(name); + if (values != null && values.hasMoreElements()) + headers.put(name, Collections.unmodifiableList(Collections.list(values))); + + }); + + return Collections.unmodifiableMap(headers); + + } + + /** + * Returns an unmodifiable Map of all HTTP parameters within the given + * request. If there are no such parameters, the returned Map will be empty. + * As there may be many values for each parameter, each value is stored as a + * separate entry in an unmodifiable List. Unlike headers, the keys of the + * returned map (parameter names) are case-sensitive. + * + * @param request + * The HTTP request to extract all parameters from. + * + * @return + * An unmodifiable Map of all HTTP parameters in the given request. + */ + private static Map> getParameters(HttpServletRequest request) { + + @SuppressWarnings("unchecked") // getParameterNames() is explicitly documented as returning a Enumeration + Enumeration names = (Enumeration) request.getParameterNames(); + + // Represent the total lack of parameters as an empty map, not null + if (names == null) + return Collections.emptyMap(); + + // Unlike headers, parameters are case-sensitive + Map> parameters = new HashMap<>(); + names.asIterator().forEachRemaining((String name) -> { + + String[] values = request.getParameterValues(name); + if (values != null && values.length != 0) + parameters.put(name, Collections.unmodifiableList(Arrays.asList(values))); + + }); + + return Collections.unmodifiableMap(parameters); + + } + + /** + * Returns an unmodifiable List of all cookies in the given HTTP request. + * + * @param request + * The HTTP request to extract all cookies from. + * + * @return + * An unmodifiable List of all cookies in the given request. + */ + private static List getCookies(HttpServletRequest request) { + + Cookie[] cookies = request.getCookies(); + if (cookies == null || cookies.length == 0) + return Collections.emptyList(); + + return Collections.unmodifiableList(Arrays.asList(cookies)); + + } + + /** + * Creates a new RequestDetails that copies the details of the given HTTP + * request. The provided request may safely go out of scope and be reused + * for future requests without rendering the content of this RequestDetails + * invalid or inaccessible. + *

+ * Though an HttpSession will be retrieved from the given request if the + * HttpSession already exists on the request, no HttpSession will be created + * through invoking this constructor. + * + * @param request + * The HTTP request to copy the details of. + */ + public RequestDetails(HttpServletRequest request) { + this.cookies = getCookies(request); + this.headers = getHeaders(request); + this.parameters = getParameters(request); + this.remoteAddress = request.getRemoteAddr(); + this.remoteHostname = request.getRemoteHost(); + this.session = request.getSession(false); + } + + /** + * Creates a new RequestDetails that copies the details of the given + * RequestDetails. + * + * @param requestDetails + * The RequestDetails to copy. + */ + public RequestDetails(RequestDetails requestDetails) { + this.cookies = requestDetails.getCookies(); + this.headers = requestDetails.getHeaders(); + this.parameters = requestDetails.getParameters(); + this.remoteAddress = requestDetails.getRemoteAddress(); + this.remoteHostname = requestDetails.getRemoteHostname(); + this.session = requestDetails.getSession(); + } + + /** + * Returns the first value stored for the given key in the given Map. If no + * such key exists in the map, or no values are associated with the given + * key, null is returned. + * + * @param map + * A Map of keys to multiple values, where each set of multiple values + * is represented by a List. + * + * @param key + * The key to look up. + * + * @return + * The first value stored for the given key in the given Map, or null + * if there is no such value or no such key. + */ + private static String getFirstValue(Map> map, String key) { + + List values = map.get(key); + if (values == null || values.isEmpty()) + return null; + + return values.get(0); + + } + + /** + * Returns the value of the HTTP header having the given name. Header names + * are case-insensitive. If no such header was present, null is returned. If + * the header had multiple values, the first value is returned. + * + * @param name + * The name of the header to retrieve. This name is case-insensitive. + * + * @return + * The first value of the HTTP header with the given name, or null if + * there is no such header. + */ + public String getHeader(String name) { + return getFirstValue(headers, name); + } + + /** + * Returns an unmodifiable List of all values of the HTTP header having the + * given name. Header names are case-insensitive. If no such header was + * present, the returned List will be empty. + * + * @param name + * The name of the header to retrieve. This name is case-insensitive. + * + * @return + * An unmodifiable List of all values of the HTTP header with the + * given name, or an empty List if there is no such header. + */ + public List getHeaders(String name) { + return headers.getOrDefault(name, Collections.emptyList()); + } + + /** + * Returns an unmodifiable Set of the names of all HTTP headers present on + * the request. If there are no headers, this set will be empty. Header + * names are case-insensitive, and the returned Set will perform lookups in + * a case-insensitive manner. + * + * @return + * An unmodifiable Set of the names of all HTTP headers present on the + * request. + */ + public Set getHeaderNames() { + return headers.keySet(); + } + + /** + * Returns an unmodifiable Map of all values of all HTTP headers on the + * request, where each Map key is a header name. Each Map value is an + * unmodifiable List of all values provided for the associated header and + * will contain at least one value. Header names are case-insensitive, and + * the returned map will perform lookups in a case-insensitive manner. + * + * @return + * An unmodifiable Map of all values of all HTTP headers on the + * request. + */ + public Map> getHeaders() { + return headers; + } + + /** + * Returns the value of the HTTP parameter having the given name. Parameter + * names are case-sensitive. If no such parameter was present, null is + * returned. If the parameter had multiple values, the first value is + * returned. + * + * @param name + * The name of the parameter to retrieve. This name is case-sensitive. + * + * @return + * The first value of the HTTP parameter with the given name, or null + * if there is no such parameter. + */ + public String getParameter(String name) { + return getFirstValue(parameters, name); + } + + /** + * Returns an unmodifiable List of all values of the HTTP parameter having + * the given name. Parameter names are case-sensitive. If no such parameter + * was present, the returned List will be empty. + * + * @param name + * The name of the parameter to retrieve. This name is case-sensitive. + * + * @return + * An unmodifiable List of all values of the HTTP parameter with the + * given name, or an empty List if there is no such parameter. + */ + public List getParameters(String name) { + return parameters.getOrDefault(name, Collections.emptyList()); + } + + /** + * Returns an unmodifiable Set of the names of all HTTP parameters present + * on the request. If there are no parameters, this set will be empty. + * Parameter names are case-sensitive, and the returned Set will perform + * lookups in a case-sensitive manner. + * + * @return + * An unmodifiable Set of the names of all HTTP parameters present on + * the request. + */ + public Set getParameterNames() { + return parameters.keySet(); + } + + /** + * Returns an unmodifiable Map of all values of all HTTP parameters on the + * request, where each Map key is a parameter name. Each Map value is an + * unmodifiable List of all values provided for the associated parameter and + * will contain at least one value. Parameter names are case-sensitive, and + * the returned map will perform lookups in a case-sensitive manner. + * + * @return + * An unmodifiable Map of all values of all HTTP parameters on the + * request. + */ + public Map> getParameters() { + return parameters; + } + + /** + * Returns an unmodifiable List of all cookies in the request. If no cookies + * are present, the returned List will be empty. + * + * @return + * An unmodifiable List of all cookies in the request, which may an + * empty List. + */ + public List getCookies() { + return cookies; + } + + /** + * Returns the HttpSession associated with the request, if any. + *

+ * NOTE: Guacamole itself does not use the HttpSession. + * The extension subsystem does not provide access to the session object + * used by Guacamole, which is considered internal. Access to an HttpSession + * is only of use if you have another application in place that + * does use HttpSession and needs to be considered. + * + * @return + * The HttpSession associated with the request, or null if there is + * no HttpSession. + */ + public HttpSession getSession() { + return session; + } + + /** + * Returns the address of the client that sent the associated request. + * + * @return + * The address of the client that sent the request. + */ + public String getRemoteAddress() { + return remoteAddress; + } + + /** + * Returns the hostname of the client that sent the associated request, if + * known. If the hostname of the client cannot be determined, the address + * will be returned instead. + * + * @return + * The hostname or address of the client that sent the request. + */ + public String getRemoteHostname() { + return remoteHostname; + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Credentials.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Credentials.java index 6ad0e240b..bbcfa48d1 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Credentials.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Credentials.java @@ -19,16 +19,15 @@ package org.apache.guacamole.net.auth; +import org.apache.guacamole.net.RequestDetails; import java.io.Serializable; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; - /** - * Simple arbitrary set of credentials, including a username/password pair, - * the HttpServletRequest associated with the request for authorization - * (if any) and the HttpSession associated with that request. - * + * Simple arbitrary set of credentials, including a username/password pair and + * a copy of the details of the HTTP request received for authentication. + *

* This class is used along with AuthenticationProvider to provide arbitrary * HTTP-based authentication for Guacamole. */ @@ -50,17 +49,9 @@ public class Credentials implements Serializable { private String password; /** - * The address of the client end of the connection which provided these - * credentials, if known. + * The details of the HTTP request that provided these Credentials. */ - private String remoteAddress; - - /** - * The hostname or, if the hostname cannot be determined, the address of - * the client end of the connection which provided these credentials, if - * known. - */ - private String remoteHostname; + private transient RequestDetails requestDetails; /** * The HttpServletRequest carrying additional credentials, if any. @@ -68,47 +59,51 @@ public class Credentials implements Serializable { private transient HttpServletRequest request; /** - * The HttpSession carrying additional credentials, if any. - */ - private transient HttpSession session; - - /** - * Construct a Credentials object with the given username, password, - * and HTTP request. The information is assigned to the various - * storage objects, and the remote hostname and address is parsed out - * of the request object. - * + * Creates a new Credentials object with the given username, password, + * and HTTP request. The details of the request are copied for later + * reference and can be retrieved with {@link #getRequestDetails()}. + * * @param username * The username that was provided for authentication. - * + * * @param password * The password that was provided for authentication. - * - * @param request + * + * @param request * The HTTP request associated with the authentication * request. */ public Credentials(String username, String password, HttpServletRequest request) { + this(username, password, new RequestDetails(request)); + this.request = request; + } + + /** + * Creates a new Credentials object with the given username, password, + * and general HTTP request details. + * + * @param username + * The username that was provided for authentication. + * + * @param password + * The password that was provided for authentication. + * + * @param requestDetails + * The details of the HTTP request associated with the authentication + * request. + */ + public Credentials(String username, String password, RequestDetails requestDetails) { this.username = username; this.password = password; - this.request = request; - - // Set the remote address - this.remoteAddress = request.getRemoteAddr(); - - // Get the remote hostname - this.remoteHostname = request.getRemoteHost(); - - // If session exists get it, but don't create a new one. - this.session = request.getSession(false); - + this.requestDetails = requestDetails; } - + /** * Returns the password associated with this set of credentials. * - * @return The password associated with this username/password pair, or - * null if no password has been set. + * @return + * The password associated with this username/password pair, or null + * if no password has been set. */ public String getPassword() { return password; @@ -117,8 +112,8 @@ public class Credentials implements Serializable { /** * Sets the password associated with this set of credentials. * - * @param password The password to associate with this username/password - * pair. + * @param password + * The password to associate with this username/password pair. */ public void setPassword(String password) { this.password = password; @@ -127,8 +122,9 @@ public class Credentials implements Serializable { /** * Returns the username associated with this set of credentials. * - * @return The username associated with this username/password pair, or - * null if no username has been set. + * @return + * The username associated with this username/password pair, or null + * if no username has been set. */ public String getUsername() { return username; @@ -137,8 +133,8 @@ public class Credentials implements Serializable { /** * Sets the username associated with this set of credentials. * - * @param username The username to associate with this username/password - * pair. + * @param username + * The username to associate with this username/password pair. */ public void setUsername(String username) { this.username = username; @@ -147,9 +143,16 @@ public class Credentials implements Serializable { /** * Returns the HttpServletRequest associated with this set of credentials. * - * @return The HttpServletRequest associated with this set of credentials, - * or null if no such request exists. + * @deprecated + * It is not reliable to reference an HttpServletRequest outside the + * scope of the specific request that created it. Use + * {@link #getRequestDetails()} instead. + * + * @return + * The HttpServletRequest associated with this set of credentials, + * or null if no such request exists. */ + @Deprecated public HttpServletRequest getRequest() { return request; } @@ -157,55 +160,173 @@ public class Credentials implements Serializable { /** * Sets the HttpServletRequest associated with this set of credentials. * - * @param request The HttpServletRequest to associated with this set of - * credentials. + * @deprecated + * It is not reliable to reference an HttpServletRequest outside the + * scope of the specific request that created it. Use + * {@link #setRequestDetails(org.apache.guacamole.net.RequestDetails)} + * instead. + * + * @param request + * The HttpServletRequest to associated with this set of credentials. */ + @Deprecated public void setRequest(HttpServletRequest request) { + setRequestDetails(new RequestDetails(request)); this.request = request; } /** - * Returns the HttpSession associated with this set of credentials. + * Returns the details of the HTTP request related to these Credentials. * - * @return The HttpSession associated with this set of credentials, or null - * if no such request exists. + * @return + * The details of the HTTP request related to these Credentials. + */ + public RequestDetails getRequestDetails() { + return requestDetails; + } + + /** + * Replaces the current HTTP request details of these Credentials with the + * given details. + * + * @param requestDetails + * The details of the HTTP request that should replace the established + * details within these Credentials. + */ + public void setRequestDetails(RequestDetails requestDetails) { + this.requestDetails = requestDetails; + } + + /** + * Returns the HttpSession associated with this set of credentials. + *

+ * This is a convenience function that is equivalent to invoking + * {@link RequestDetails#getSession()} on the {@link RequestDetails} + * returned by {@link #getRequestDetails()}. + *

+ * NOTE: Guacamole itself does not use the HttpSession. + * The extension subsystem does not provide access to the session object + * used by Guacamole, which is considered internal. Access to an HttpSession + * is only of use if you have another application in place that + * does use HttpSession and needs to be considered. + * + * @return + * The HttpSession associated with this set of credentials, or null if + * there is no HttpSession. */ public HttpSession getSession() { - return session; + return requestDetails.getSession(); } /** * Sets the HttpSession associated with this set of credentials. * - * @param session The HttpSession to associated with this set of - * credentials. + * @deprecated + * Since 1.6.0, the HttpSession that may be associated with a + * Credentials object is tied to the RequestDetails. If the HttpSession + * is part of a Credentials and truly needs to be replaced by another + * HttpSession, use {@link #setRequestDetails(org.apache.guacamole.net.RequestDetails)} + * to override the underlying {@link RequestDetails} instead. + * + * @param session + * The HttpSession to associated with this set of credentials. */ + @Deprecated public void setSession(HttpSession session) { - this.session = session; + setRequestDetails(new RequestDetails(getRequestDetails()) { + + @Override + public HttpSession getSession() { + return session; + } + + }); + } + + /** + * Returns the value of the HTTP header having the given name from the + * original details of the HTTP request that is related to these + * credentials. Header names are case-insensitive. If no such header was + * present, null is returned. If the header had multiple values, the first + * value is returned. + *

+ * For access to all values of a header, as well as other details of the + * request, see {@link #getRequestDetails()}. This is a convenience + * function that is equivalent to invoking {@link RequestDetails#getHeader(java.lang.String)}. + * + * @param name + * The name of the header to retrieve. This name is case-insensitive. + * + * @return + * The first value of the HTTP header with the given name, or null if + * there is no such header. + */ + public String getHeader(String name) { + return getRequestDetails().getHeader(name); + } + + /** + * Returns the value of the HTTP parameter having the given name from the + * original details of the HTTP request that is related to these + * credentials. Parameter names are case-sensitive. If no such parameter was + * present, null is returned. If the parameter had multiple values, the + * first value is returned. + *

+ * For access to all values of a parameter, as well as other details of the + * request, see {@link #getRequestDetails()}. This is a convenience + * function that is equivalent to invoking {@link RequestDetails#getParameter(java.lang.String)}. + * + * @param name + * The name of the parameter to retrieve. This name is case-sensitive. + * + * @return + * The first value of the HTTP parameter with the given name, or null + * if there is no such parameter. + */ + public String getParameter(String name) { + return getRequestDetails().getParameter(name); } /** * Returns the address of the client end of the connection which provided * these credentials, if known. + *

+ * This is a convenience function that is equivalent to invoking + * {@link RequestDetails#getRemoteAddress()} on the + * {@link RequestDetails} returned by {@link #getRequestDetails()}. * * @return * The address of the client end of the connection which provided these * credentials, or null if the address is not known. */ public String getRemoteAddress() { - return remoteAddress; + return getRequestDetails().getRemoteAddress(); } /** * Sets the address of the client end of the connection which provided * these credentials. * + * @deprecated + * Since 1.6.0, the address that may be associated with a Credentials + * object is tied to the RequestDetails. If the address truly needs to + * be replaced, use {@link #setRequestDetails(org.apache.guacamole.net.RequestDetails)} + * to override the underlying {@link RequestDetails} instead. + * * @param remoteAddress * The address of the client end of the connection which provided these * credentials, or null if the address is not known. */ + @Deprecated public void setRemoteAddress(String remoteAddress) { - this.remoteAddress = remoteAddress; + setRequestDetails(new RequestDetails(getRequestDetails()) { + + @Override + public String getRemoteAddress() { + return remoteAddress; + } + + }); } /** @@ -213,13 +334,17 @@ public class Credentials implements Serializable { * these credentials, if known. If the hostname of the client cannot be * determined, but the address is known, the address may be returned * instead. + *

+ * This is a convenience function that is equivalent to invoking + * {@link RequestDetails#getRemoteHostname()} on the + * {@link RequestDetails} returned by {@link #getRequestDetails()}. * * @return * The hostname or address of the client end of the connection which * provided these credentials, or null if the hostname is not known. */ public String getRemoteHostname() { - return remoteHostname; + return getRequestDetails().getRemoteHostname(); } /** @@ -228,12 +353,26 @@ public class Credentials implements Serializable { * determined, but the address is known, the address may be specified * instead. * + * @deprecated + * Since 1.6.0, the hostname that may be associated with a Credentials + * object is tied to the RequestDetails. If the hostname truly needs to + * be replaced, use {@link #setRequestDetails(org.apache.guacamole.net.RequestDetails)} + * to override the underlying {@link RequestDetails} instead. + * * @param remoteHostname * The hostname or address of the client end of the connection which * provided these credentials, or null if the hostname is not known. */ + @Deprecated public void setRemoteHostname(String remoteHostname) { - this.remoteHostname = remoteHostname; + setRequestDetails(new RequestDetails(getRequestDetails()) { + + @Override + public String getRemoteHostname() { + return remoteHostname; + } + + }); } /** @@ -256,15 +395,9 @@ public class Credentials implements Serializable { if (getUsername() != null || getPassword() != null) return false; - // All further tests depend on HTTP request details - HttpServletRequest httpRequest = getRequest(); - if (httpRequest == null) - return true; - // An authentication request is non-empty if it contains any HTTP // parameters at all or contains an authentication token - return !httpRequest.getParameterNames().hasMoreElements() - && httpRequest.getHeader("Guacamole-Token") == null; + return getRequestDetails().getParameterNames().isEmpty() && getHeader("Guacamole-Token") == null; } diff --git a/guacamole/src/main/java/org/apache/guacamole/event/RemoteAddress.java b/guacamole/src/main/java/org/apache/guacamole/event/RemoteAddress.java index f55602f59..324a56886 100644 --- a/guacamole/src/main/java/org/apache/guacamole/event/RemoteAddress.java +++ b/guacamole/src/main/java/org/apache/guacamole/event/RemoteAddress.java @@ -20,7 +20,6 @@ package org.apache.guacamole.event; import java.util.regex.Pattern; -import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.net.auth.Credentials; /** @@ -78,17 +77,15 @@ public class RemoteAddress implements LoggableDetail { @Override public String toString() { - HttpServletRequest request = creds.getRequest(); - if (request == null) - return creds.getRemoteAddress(); + String remoteAddress = creds.getRemoteAddress(); // Log X-Forwarded-For, if present and valid - String header = request.getHeader("X-Forwarded-For"); + String header = creds.getHeader("X-Forwarded-For"); if (header != null && X_FORWARDED_FOR.matcher(header).matches()) - return "[" + header + ", " + request.getRemoteAddr() + "]"; + return "[" + header + ", " + remoteAddress + "]"; // If header absent or invalid, just use source IP - return request.getRemoteAddr(); + return remoteAddress; }