diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java index c41114c02..20d465803 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java @@ -223,5 +223,10 @@ public class ConnectedLDAPConfiguration implements LDAPConfiguration, AutoClosea public MemberAttributeType getMemberAttributeType() throws GuacamoleException { return config.getMemberAttributeType(); } + + @Override + public boolean getNestedGroups() throws GuacamoleException { + return config.getNestedGroups(); + } } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java index 217964382..1cbf41ed0 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java @@ -151,4 +151,9 @@ public class DefaultLDAPConfiguration implements LDAPConfiguration { return MemberAttributeType.DN; } + @Override + public boolean getNestedGroups() { + return false; + } + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java index 5ffeb203b..1cfffa589 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java @@ -234,4 +234,12 @@ public class EnvironmentLDAPConfiguration implements LDAPConfiguration { ); } + @Override + public boolean getNestedGroups() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_NESTED_GROUPS, + DEFAULT.getNestedGroups() + ); + } + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java index bddccd871..88a20dfe3 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java @@ -204,6 +204,13 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { @JsonProperty("member-attribute-type") private String memberAttributeType; + /** + * The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_NESTED_GROUPS}. + * If not set within the YAML, this will be false. + */ + @JsonProperty("nested-groups") + private Boolean nestedGroups; + /** * The default configuration options for all parameters. */ @@ -434,6 +441,11 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { return withDefault(memberAttribute, defaultConfig::getMemberAttribute); } + @Override + public boolean getNestedGroups() throws GuacamoleException { + return withDefault(nestedGroups, defaultConfig::getNestedGroups); + } + @Override public MemberAttributeType getMemberAttributeType() throws GuacamoleException { return withDefault(LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE_TYPE, diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java index abbf9103a..4c56d4400 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java @@ -334,4 +334,16 @@ public interface LDAPConfiguration { */ MemberAttributeType getMemberAttributeType() throws GuacamoleException; + /** + * Returns whether nested groups should be included in group membership. + * + * @return + * Whether to search in nested groups. + * + * @throws GuacamoleException + * If the configuration information determining whether nested + * groups should be used cannot be retrieved. + */ + boolean getNestedGroups() throws GuacamoleException; + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java index 7349356b9..10c0693f7 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java @@ -307,4 +307,15 @@ public class LDAPGuacamoleProperties { }; + /** + * Whether or not to search nested groups. + */ + public static final BooleanGuacamoleProperty LDAP_NESTED_GROUPS = + new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "ldap-nested-groups"; } + + }; + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java index b38fc9198..a0492cb65 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/group/UserGroupService.java @@ -27,10 +27,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.entry.Value; import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; import org.apache.directory.api.ldap.model.filter.AndNode; import org.apache.directory.api.ldap.model.filter.EqualityNode; import org.apache.directory.api.ldap.model.filter.ExprNode; +import org.apache.directory.api.ldap.model.filter.ExtensibleNode; import org.apache.directory.api.ldap.model.filter.NotNode; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.guacamole.auth.ldap.conf.MemberAttributeType; @@ -55,6 +57,11 @@ public class UserGroupService { */ private static final Logger logger = LoggerFactory.getLogger(UserGroupService.class); + /** + * Constant for nested LDAP group matching in Active Directory + */ + private final String LDAP_GROUP_NESTED_MATCHING_OID = "1.2.840.113556.1.4.1941"; + /** * Service for executing LDAP queries. */ @@ -227,14 +234,36 @@ public class UserGroupService { groupAttributes.add(memberAttribute); // Get all groups the user is a member of starting at the groupBaseDN, - // excluding guacConfigGroups + // excluding guacConfigGroups and evaluating nested groups + // (if enabled). + + ExprNode groupFilter = config.getGroupSearchFilter(); + String filterValue = userIDorDN; + + if (config.getNestedGroups()) { + + // Add support for nested groups using LDAP_MATCHING_RULE_IN_CHAIN + // (memberOf:1.2.840.113556.1.4.1941:=) + // Matching rule OID for LDAP_MATCHING_RULE_IN_CHAIN + // ** This possibly only supports Active Directory ** + ExtensibleNode node = new ExtensibleNode("member"); + filterValue = null; + + // Explicitly set the matching rule ID and dnAttributes + node.setMatchingRuleId(LDAP_GROUP_NESTED_MATCHING_OID); + node.setDnAttributes(false); + node.setValue(new Value(userIDorDN)); + groupFilter = new AndNode( + groupFilter, node + ); + } return queryService.search( config, config.getLDAPConnection(), groupBaseDN, - getGroupSearchFilter(config), + groupFilter, Collections.singleton(memberAttribute), - userIDorDN, + filterValue, groupAttributes );