mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUACAMOLE-103: Periodically clean SAMLResponseMap for expired responses.
This commit is contained in:
@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.saml;
|
|||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
import com.onelogin.saml2.Auth;
|
||||||
import com.onelogin.saml2.authn.AuthnRequest;
|
import com.onelogin.saml2.authn.AuthnRequest;
|
||||||
import com.onelogin.saml2.authn.SamlResponse;
|
import com.onelogin.saml2.authn.SamlResponse;
|
||||||
import com.onelogin.saml2.exception.SettingsException;
|
import com.onelogin.saml2.exception.SettingsException;
|
||||||
@@ -29,6 +30,11 @@ import com.onelogin.saml2.settings.Saml2Settings;
|
|||||||
import com.onelogin.saml2.util.Util;
|
import com.onelogin.saml2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.xpath.XPathExpressionException;
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
@@ -129,9 +135,22 @@ public class AuthenticationProviderService {
|
|||||||
// Grab the username, and, if present, finish authentication.
|
// Grab the username, and, if present, finish authentication.
|
||||||
String username = samlResponse.getNameId().toLowerCase();
|
String username = samlResponse.getNameId().toLowerCase();
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
|
|
||||||
|
// Retrieve any provided attributes
|
||||||
|
Map<String, List<String>> attributes =
|
||||||
|
samlResponse.getAttributes();
|
||||||
|
|
||||||
|
// Back-port the username to the credentials
|
||||||
credentials.setUsername(username);
|
credentials.setUsername(username);
|
||||||
SAMLAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
|
|
||||||
authenticatedUser.init(username, credentials);
|
// Configure the AuthenticatedUser and return it
|
||||||
|
SAMLAuthenticatedUser authenticatedUser =
|
||||||
|
authenticatedUserProvider.get();
|
||||||
|
|
||||||
|
authenticatedUser.init(username, credentials,
|
||||||
|
parseTokens(attributes),
|
||||||
|
new HashSet<>(attributes.get(confService.getGroupAttribute())));
|
||||||
|
|
||||||
return authenticatedUser;
|
return authenticatedUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,10 +218,25 @@ public class AuthenticationProviderService {
|
|||||||
// Redirect to SAML Identity Provider (IdP)
|
// Redirect to SAML Identity Provider (IdP)
|
||||||
throw new GuacamoleInsufficientCredentialsException("Redirecting to SAML IdP.",
|
throw new GuacamoleInsufficientCredentialsException("Redirecting to SAML IdP.",
|
||||||
new CredentialsInfo(Arrays.asList(new Field[] {
|
new CredentialsInfo(Arrays.asList(new Field[] {
|
||||||
new SAMLRedirectField(reqString)
|
new RedirectField("samlRedirect", reqString, "LOGIN.REDIRECT_PENDING")
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, String> parseTokens(Map<String, List<String>> attributes)
|
||||||
|
throws GuacamoleException {
|
||||||
|
|
||||||
|
Map<String, String> tokens = new HashMap<>();
|
||||||
|
for (Entry<String, List<String>> entry : attributes.entrySet()) {
|
||||||
|
|
||||||
|
List<String> values = entry.getValue();
|
||||||
|
tokens.put(entry.getKey(), values.get(0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,8 +21,13 @@ package org.apache.guacamole.auth.saml;
|
|||||||
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import com.onelogin.saml2.authn.SamlResponse;
|
import com.onelogin.saml2.authn.SamlResponse;
|
||||||
|
import com.onelogin.saml2.exception.ValidationError;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that handles mapping of hashes to SAMLResponse objects.
|
* A class that handles mapping of hashes to SAMLResponse objects.
|
||||||
@@ -37,6 +42,21 @@ public class SAMLResponseMap {
|
|||||||
private final ConcurrentMap<String, SamlResponse> samlResponseMap =
|
private final ConcurrentMap<String, SamlResponse> samlResponseMap =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executor service which runs the periodic cleanup task
|
||||||
|
*/
|
||||||
|
private final ScheduledExecutorService executor =
|
||||||
|
Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of this response map and kick off the executor
|
||||||
|
* that schedules the response cleanup task to run every five minutes.
|
||||||
|
*/
|
||||||
|
public SAMLResponseMap() {
|
||||||
|
// Cleanup unclaimed responses every five minutes
|
||||||
|
executor.scheduleAtFixedRate(new SAMLResponseCleanupTask(), 5, 5, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the SamlResponse from the map that is represented by the
|
* Retrieve the SamlResponse from the map that is represented by the
|
||||||
* provided hash, or null if no such object exists.
|
* provided hash, or null if no such object exists.
|
||||||
@@ -77,4 +97,27 @@ public class SAMLResponseMap {
|
|||||||
return samlResponseMap.containsKey(hash);
|
return samlResponseMap.containsKey(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task which runs every five minutes and cleans up any expired SAML
|
||||||
|
* responses that haven't been claimed and removed from the map.
|
||||||
|
*/
|
||||||
|
private class SAMLResponseCleanupTask implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
// Loop through responses in map and remove ones that are no longer valid.
|
||||||
|
for (Entry<String, SamlResponse> entry : samlResponseMap.entrySet()) {
|
||||||
|
try {
|
||||||
|
entry.getValue().validateTimestamps();
|
||||||
|
}
|
||||||
|
catch (ValidationError e) {
|
||||||
|
samlResponseMap.remove(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,7 @@ import org.apache.guacamole.GuacamoleServerException;
|
|||||||
import org.apache.guacamole.environment.Environment;
|
import org.apache.guacamole.environment.Environment;
|
||||||
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
|
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
|
||||||
import org.apache.guacamole.properties.FileGuacamoleProperty;
|
import org.apache.guacamole.properties.FileGuacamoleProperty;
|
||||||
|
import org.apache.guacamole.properties.StringGuacamoleProperty;
|
||||||
import org.apache.guacamole.properties.URIGuacamoleProperty;
|
import org.apache.guacamole.properties.URIGuacamoleProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,6 +143,18 @@ public class ConfigurationService {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property that defines what attribute the SAML provider will return
|
||||||
|
* that contains group membership for the authenticated user.
|
||||||
|
*/
|
||||||
|
private static final StringGuacamoleProperty SAML_GROUP_ATTRIBUTE =
|
||||||
|
new StringGuacamoleProperty() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() { return "saml-group-attribute"; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Guacamole server environment.
|
* The Guacamole server environment.
|
||||||
*/
|
*/
|
||||||
@@ -288,6 +301,20 @@ public class ConfigurationService {
|
|||||||
return environment.getProperty(SAML_COMPRESS_RESPONSE, true);
|
return environment.getProperty(SAML_COMPRESS_RESPONSE, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the attribute that will be supplied by the identity
|
||||||
|
* provider that contains the groups of which this user is a member.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The name of the attribute that contains the user groups.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If guacamole.properties cannot be parsed.
|
||||||
|
*/
|
||||||
|
public String getGroupAttribute() throws GuacamoleException {
|
||||||
|
return environment.getProperty(SAML_GROUP_ATTRIBUTE, "groups");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the collection of SAML settings used to
|
* Returns the collection of SAML settings used to
|
||||||
* initialize the client.
|
* initialize the client.
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
package org.apache.guacamole.auth.saml.user;
|
package org.apache.guacamole.auth.saml.user;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
|
import org.apache.guacamole.net.auth.AbstractAuthenticatedUser;
|
||||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||||
import org.apache.guacamole.net.auth.Credentials;
|
import org.apache.guacamole.net.auth.Credentials;
|
||||||
@@ -43,6 +45,16 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
*/
|
*/
|
||||||
private Credentials credentials;
|
private Credentials credentials;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The effective groups of the authenticated user.
|
||||||
|
*/
|
||||||
|
private Set<String> effectiveGroups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokens associated with the authenticated user.
|
||||||
|
*/
|
||||||
|
private Map<String, String> tokens;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this AuthenticatedUser using the given username and
|
* Initializes this AuthenticatedUser using the given username and
|
||||||
* credentials.
|
* credentials.
|
||||||
@@ -52,12 +64,31 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
*
|
*
|
||||||
* @param credentials
|
* @param credentials
|
||||||
* The credentials provided when this user was authenticated.
|
* The credentials provided when this user was authenticated.
|
||||||
|
*
|
||||||
|
* @param tokens
|
||||||
|
* The tokens available from this authentication provider.
|
||||||
|
*
|
||||||
|
* @param effectiveGroups
|
||||||
|
* The groups of which this user is a member.
|
||||||
*/
|
*/
|
||||||
public void init(String username, Credentials credentials) {
|
public void init(String username, Credentials credentials,
|
||||||
|
Map<String, String> tokens, Set<String> effectiveGroups) {
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
|
this.effectiveGroups = effectiveGroups;
|
||||||
|
this.tokens = tokens;
|
||||||
setIdentifier(username);
|
setIdentifier(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tokens associated with this particular user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A map of token names and values available from this user account.
|
||||||
|
*/
|
||||||
|
public Map<String, String> getTokens() {
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationProvider getAuthenticationProvider() {
|
public AuthenticationProvider getAuthenticationProvider() {
|
||||||
return authProvider;
|
return authProvider;
|
||||||
@@ -68,4 +99,9 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getEffectiveUserGroups() {
|
||||||
|
return effectiveGroups;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user