mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +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.Provider;
|
||||
import com.onelogin.saml2.Auth;
|
||||
import com.onelogin.saml2.authn.AuthnRequest;
|
||||
import com.onelogin.saml2.authn.SamlResponse;
|
||||
import com.onelogin.saml2.exception.SettingsException;
|
||||
@@ -29,6 +30,11 @@ import com.onelogin.saml2.settings.Saml2Settings;
|
||||
import com.onelogin.saml2.util.Util;
|
||||
import java.io.IOException;
|
||||
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.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
@@ -129,9 +135,22 @@ public class AuthenticationProviderService {
|
||||
// Grab the username, and, if present, finish authentication.
|
||||
String username = samlResponse.getNameId().toLowerCase();
|
||||
if (username != null) {
|
||||
|
||||
// Retrieve any provided attributes
|
||||
Map<String, List<String>> attributes =
|
||||
samlResponse.getAttributes();
|
||||
|
||||
// Back-port the username to the credentials
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -199,10 +218,25 @@ public class AuthenticationProviderService {
|
||||
// Redirect to SAML Identity Provider (IdP)
|
||||
throw new GuacamoleInsufficientCredentialsException("Redirecting to SAML IdP.",
|
||||
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.onelogin.saml2.authn.SamlResponse;
|
||||
import com.onelogin.saml2.exception.ValidationError;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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.
|
||||
@@ -37,6 +42,21 @@ public class SAMLResponseMap {
|
||||
private final ConcurrentMap<String, SamlResponse> samlResponseMap =
|
||||
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
|
||||
* provided hash, or null if no such object exists.
|
||||
@@ -77,4 +97,27 @@ public class SAMLResponseMap {
|
||||
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.properties.BooleanGuacamoleProperty;
|
||||
import org.apache.guacamole.properties.FileGuacamoleProperty;
|
||||
import org.apache.guacamole.properties.StringGuacamoleProperty;
|
||||
import org.apache.guacamole.properties.URIGuacamoleProperty;
|
||||
|
||||
/**
|
||||
@@ -141,6 +142,18 @@ public class ConfigurationService {
|
||||
public String getName() { return "saml-strict"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -287,6 +300,20 @@ public class ConfigurationService {
|
||||
private Boolean getCompressResponse() throws GuacamoleException {
|
||||
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
|
||||
|
@@ -20,6 +20,8 @@
|
||||
package org.apache.guacamole.auth.saml.user;
|
||||
|
||||
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.AuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
@@ -42,6 +44,16 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
||||
* The credentials provided when this user was authenticated.
|
||||
*/
|
||||
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
|
||||
@@ -52,11 +64,30 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
||||
*
|
||||
* @param credentials
|
||||
* 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.effectiveGroups = effectiveGroups;
|
||||
this.tokens = tokens;
|
||||
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
|
||||
public AuthenticationProvider getAuthenticationProvider() {
|
||||
@@ -67,5 +98,10 @@ public class SAMLAuthenticatedUser extends AbstractAuthenticatedUser {
|
||||
public Credentials getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getEffectiveUserGroups() {
|
||||
return effectiveGroups;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user