mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 13:41:21 +00:00
GUACAMOLE-793: validateTicket() returns the CASAuthenticatedUser instance rather than just a token so CAS Provider can return Group - like LDAP Provider
This commit is contained in:
committed by
Michael Jumper
parent
ecd385b0f8
commit
7b8dc36644
@@ -20,9 +20,9 @@
|
|||||||
package org.apache.guacamole.auth.cas;
|
package org.apache.guacamole.auth.cas;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.apache.guacamole.form.Field;
|
import org.apache.guacamole.form.Field;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
@@ -34,6 +34,8 @@ import org.apache.guacamole.auth.cas.form.CASTicketField;
|
|||||||
import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
|
import org.apache.guacamole.auth.cas.ticket.TicketValidationService;
|
||||||
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser;
|
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser;
|
||||||
import org.apache.guacamole.language.TranslatableMessage;
|
import org.apache.guacamole.language.TranslatableMessage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service providing convenience functions for the CAS AuthenticationProvider
|
* Service providing convenience functions for the CAS AuthenticationProvider
|
||||||
@@ -41,6 +43,11 @@ import org.apache.guacamole.language.TranslatableMessage;
|
|||||||
*/
|
*/
|
||||||
public class AuthenticationProviderService {
|
public class AuthenticationProviderService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for retrieving CAS configuration information.
|
* Service for retrieving CAS configuration information.
|
||||||
*/
|
*/
|
||||||
@@ -53,12 +60,6 @@ public class AuthenticationProviderService {
|
|||||||
@Inject
|
@Inject
|
||||||
private TicketValidationService ticketService;
|
private TicketValidationService ticketService;
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider for AuthenticatedUser objects.
|
|
||||||
*/
|
|
||||||
@Inject
|
|
||||||
private Provider<CASAuthenticatedUser> authenticatedUserProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an AuthenticatedUser representing the user authenticated by the
|
* Returns an AuthenticatedUser representing the user authenticated by the
|
||||||
* given credentials.
|
* given credentials.
|
||||||
@@ -82,13 +83,7 @@ public class AuthenticationProviderService {
|
|||||||
if (request != null) {
|
if (request != null) {
|
||||||
String ticket = request.getParameter(CASTicketField.PARAMETER_NAME);
|
String ticket = request.getParameter(CASTicketField.PARAMETER_NAME);
|
||||||
if (ticket != null) {
|
if (ticket != null) {
|
||||||
Map<String, String> tokens = ticketService.validateTicket(ticket, credentials);
|
return ticketService.validateTicket(ticket, credentials);
|
||||||
String username = credentials.getUsername();
|
|
||||||
if (username != null) {
|
|
||||||
CASAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
|
|
||||||
authenticatedUser.init(username, credentials, tokens);
|
|
||||||
return authenticatedUser;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
package org.apache.guacamole.auth.cas.conf;
|
package org.apache.guacamole.auth.cas.conf;
|
||||||
|
|
||||||
import org.apache.guacamole.properties.URIGuacamoleProperty;
|
import org.apache.guacamole.properties.URIGuacamoleProperty;
|
||||||
|
import org.apache.guacamole.properties.StringGuacamoleProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides properties required for use of the CAS authentication provider.
|
* Provides properties required for use of the CAS authentication provider.
|
||||||
@@ -68,5 +69,28 @@ public class CASGuacamoleProperties {
|
|||||||
public String getName() { return "cas-clearpass-key"; }
|
public String getName() { return "cas-clearpass-key"; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attribute used for group membership
|
||||||
|
* example: memberOf (case sensitive)
|
||||||
|
*/
|
||||||
|
public static final StringGuacamoleProperty CAS_GROUP_ATTRIBUTE =
|
||||||
|
new StringGuacamoleProperty() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() { return "cas-group-attribute"; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute used for group membership, such as "memberOf".
|
||||||
|
* This attribute is case sensitive.
|
||||||
|
*/
|
||||||
|
public static final StringGuacamoleProperty CAS_GROUP_DN_FORMAT =
|
||||||
|
new StringGuacamoleProperty() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() { return "cas-group-dn-format"; }
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -85,4 +85,40 @@ public class ConfigurationService {
|
|||||||
return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY);
|
return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attribute used to determine group memberships
|
||||||
|
* in CAS, or null if not defined.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The attribute name used to determine group memberships in CAS,
|
||||||
|
* null if not defined.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If guacamole.properties cannot be parsed.
|
||||||
|
*/
|
||||||
|
public String getGroupAttribute() throws GuacamoleException {
|
||||||
|
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full DN specification used to format group DN's in CAS,
|
||||||
|
* or null if not defined.
|
||||||
|
* If CAS is backed by LDAP, it will return an LDAP DN, such as
|
||||||
|
* CN=foo,OU=bar,DC=example,DC=com. This DN specification may be set to
|
||||||
|
* CN=%s,OU=bar,DC=example,DC=com and given the example above, would result
|
||||||
|
* in a group called "foo". CAS backed by something other than LDAP would
|
||||||
|
* likely not need this.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The DN format specification.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If guacamole.properties cannot be parsed.
|
||||||
|
*/
|
||||||
|
public String getGroupDnFormat() throws GuacamoleException {
|
||||||
|
return environment.getProperty(CASGuacamoleProperties.CAS_GROUP_DN_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.cas.ticket;
|
|||||||
|
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -33,10 +34,15 @@ import java.nio.charset.Charset;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.GuacamoleSecurityException;
|
import org.apache.guacamole.GuacamoleSecurityException;
|
||||||
import org.apache.guacamole.GuacamoleServerException;
|
import org.apache.guacamole.GuacamoleServerException;
|
||||||
import org.apache.guacamole.auth.cas.conf.ConfigurationService;
|
import org.apache.guacamole.auth.cas.conf.ConfigurationService;
|
||||||
|
import org.apache.guacamole.auth.cas.user.CASAuthenticatedUser;
|
||||||
import org.apache.guacamole.net.auth.Credentials;
|
import org.apache.guacamole.net.auth.Credentials;
|
||||||
import org.apache.guacamole.token.TokenName;
|
import org.apache.guacamole.token.TokenName;
|
||||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||||
@@ -68,6 +74,12 @@ public class TicketValidationService {
|
|||||||
@Inject
|
@Inject
|
||||||
private ConfigurationService confService;
|
private ConfigurationService confService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for AuthenticatedUser objects.
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
private Provider<CASAuthenticatedUser> authenticatedUserProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and parses the given ID ticket, returning a map of all
|
* Validates and parses the given ID ticket, returning a map of all
|
||||||
* available tokens for the given user based on attributes provided by the
|
* available tokens for the given user based on attributes provided by the
|
||||||
@@ -81,14 +93,13 @@ public class TicketValidationService {
|
|||||||
* password values in.
|
* password values in.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* A Map all of tokens for the user parsed from attributes returned
|
* A CASAuthenticatedUser instance containing the ticket data returned by the CAS server.
|
||||||
* by the CAS server.
|
|
||||||
*
|
*
|
||||||
* @throws GuacamoleException
|
* @throws GuacamoleException
|
||||||
* If the ID ticket is not valid or guacamole.properties could
|
* If the ID ticket is not valid or guacamole.properties could
|
||||||
* not be parsed.
|
* not be parsed.
|
||||||
*/
|
*/
|
||||||
public Map<String, String> validateTicket(String ticket,
|
public CASAuthenticatedUser validateTicket(String ticket,
|
||||||
Credentials credentials) throws GuacamoleException {
|
Credentials credentials) throws GuacamoleException {
|
||||||
|
|
||||||
// Retrieve the configured CAS URL, establish a ticket validator,
|
// Retrieve the configured CAS URL, establish a ticket validator,
|
||||||
@@ -100,6 +111,7 @@ public class TicketValidationService {
|
|||||||
validator.setEncoding("UTF-8");
|
validator.setEncoding("UTF-8");
|
||||||
try {
|
try {
|
||||||
Map<String, String> tokens = new HashMap<>();
|
Map<String, String> tokens = new HashMap<>();
|
||||||
|
Set<String> effectiveGroups = new HashSet<>();
|
||||||
URI confRedirectURI = confService.getRedirectURI();
|
URI confRedirectURI = confService.getRedirectURI();
|
||||||
Assertion a = validator.validate(ticket, confRedirectURI.toString());
|
Assertion a = validator.validate(ticket, confRedirectURI.toString());
|
||||||
AttributePrincipal principal = a.getPrincipal();
|
AttributePrincipal principal = a.getPrincipal();
|
||||||
@@ -122,16 +134,44 @@ public class TicketValidationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert remaining attributes that have values to Strings
|
// Convert remaining attributes that have values to Strings
|
||||||
|
String groupAttribute = confService.getGroupAttribute();
|
||||||
|
// Use cas-member-attribute to retrieve and set group memberships
|
||||||
|
String groupDnFormat = confService.getGroupDnFormat();
|
||||||
|
String groupTemplate = "";
|
||||||
|
if (groupDnFormat != null) {
|
||||||
|
// if CAS is backended to LDAP, groups come in as RFC4514 DN
|
||||||
|
// syntax. If cas-group-dn-format is set, this strips an
|
||||||
|
// entry such as "CN=Foo,OU=Bar,DC=example,DC=com" to "Foo"
|
||||||
|
groupTemplate = groupDnFormat.replace("%s","([A-Za-z0-9_\\(\\)\\-\\.\\s+]+)");
|
||||||
|
// the underlying parser aggregates all instances of the same
|
||||||
|
// attribute, so we need to be able to parse them out
|
||||||
|
groupTemplate=groupTemplate+",*\\s*";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
groupTemplate = "([A-Za-z0-9_\\(\\)\\-\\.\\s+]+,*\\s*)";
|
||||||
|
}
|
||||||
|
Pattern pattern = Pattern.compile(groupTemplate);
|
||||||
|
|
||||||
for (Entry <String, Object> attr : ticketAttrs.entrySet()) {
|
for (Entry <String, Object> attr : ticketAttrs.entrySet()) {
|
||||||
String tokenName = TokenName.canonicalize(attr.getKey(),
|
String tokenName = TokenName.canonicalize(attr.getKey(),
|
||||||
CAS_ATTRIBUTE_TOKEN_PREFIX);
|
CAS_ATTRIBUTE_TOKEN_PREFIX);
|
||||||
Object value = attr.getValue();
|
Object value = attr.getValue();
|
||||||
if (value != null)
|
if (value != null) {
|
||||||
tokens.put(tokenName, value.toString());
|
String attrValue = value.toString();
|
||||||
|
tokens.put(tokenName, attrValue);
|
||||||
|
if (attr.getKey().equals(groupAttribute)) {
|
||||||
|
Matcher matcher =
|
||||||
|
pattern.matcher(attrValue.substring(1,attrValue.length()-1));
|
||||||
|
while (matcher.find()) {
|
||||||
|
effectiveGroups.add(matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokens;
|
CASAuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
|
||||||
|
authenticatedUser.init(username, credentials, tokens, effectiveGroups);
|
||||||
|
return authenticatedUser;
|
||||||
}
|
}
|
||||||
catch (TicketValidationException e) {
|
catch (TicketValidationException e) {
|
||||||
throw new GuacamoleException("Ticket validation failed.", e);
|
throw new GuacamoleException("Ticket validation failed.", e);
|
||||||
|
@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.cas.user;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
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;
|
||||||
@@ -50,6 +51,11 @@ public class CASAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
*/
|
*/
|
||||||
private Map<String, String> tokens;
|
private Map<String, String> tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique identifiers of all user groups which this user is a member of.
|
||||||
|
*/
|
||||||
|
private Set<String> effectiveGroups;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this AuthenticatedUser using the given username and
|
* Initializes this AuthenticatedUser using the given username and
|
||||||
* credentials, and an empty map of parameter tokens.
|
* credentials, and an empty map of parameter tokens.
|
||||||
@@ -61,7 +67,7 @@ public class CASAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
* The credentials provided when this user was authenticated.
|
* The credentials provided when this user was authenticated.
|
||||||
*/
|
*/
|
||||||
public void init(String username, Credentials credentials) {
|
public void init(String username, Credentials credentials) {
|
||||||
this.init(username, credentials, Collections.emptyMap());
|
this.init(username, credentials, Collections.emptyMap(), Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,9 +85,10 @@ public class CASAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
* as tokens when connections are established with this user.
|
* as tokens when connections are established with this user.
|
||||||
*/
|
*/
|
||||||
public void init(String username, Credentials credentials,
|
public void init(String username, Credentials credentials,
|
||||||
Map<String, String> tokens) {
|
Map<String, String> tokens, Set<String> effectiveGroups) {
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
this.tokens = Collections.unmodifiableMap(tokens);
|
this.tokens = Collections.unmodifiableMap(tokens);
|
||||||
|
this.effectiveGroups = effectiveGroups;
|
||||||
setIdentifier(username.toLowerCase());
|
setIdentifier(username.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,4 +114,9 @@ public class CASAuthenticatedUser extends AbstractAuthenticatedUser {
|
|||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getEffectiveUserGroups() {
|
||||||
|
return effectiveGroups;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user