Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@

@Slf4j
public class LdapAuthenticator implements AuthenticatorHandler {
static final String AD_RECURSIVE_GROUP_MATCHING_RULE = "1.2.840.113556.1.4.1941";
static final String LDAP_ERR_MSG = "[LDAP] Issue in creating a LookUp Connection ";
private static final int MAX_RETRIES = 3;
private static final int BASE_DELAY_MS = 500;
Expand Down Expand Up @@ -397,8 +398,7 @@ private void getRoleForLdap(String userDn, User user, Boolean reAssign)
Filter.createEqualityFilter(
ldapConfiguration.getGroupAttributeName(),
ldapConfiguration.getGroupAttributeValue());
Filter groupMemberAttr =
Filter.createEqualityFilter(ldapConfiguration.getGroupMemberAttributeName(), userDn);
Filter groupMemberAttr = buildGroupMemberFilter(ldapConfiguration, userDn);
Filter groupAndMemberFilter = Filter.createANDFilter(groupFilter, groupMemberAttr);
SearchRequest searchRequest =
new SearchRequest(
Expand Down Expand Up @@ -474,6 +474,18 @@ private void getRoleForLdap(String userDn, User user, Boolean reAssign)
}
}

static Filter buildGroupMemberFilter(LdapConfiguration ldapConfiguration, String userDn) {
if (Boolean.TRUE.equals(ldapConfiguration.getRecursiveGroupMembership())) {
return Filter.createExtensibleMatchFilter(
ldapConfiguration.getGroupMemberAttributeName(),
AD_RECURSIVE_GROUP_MATCHING_RULE,
false,
userDn);
}

return Filter.createEqualityFilter(ldapConfiguration.getGroupMemberAttributeName(), userDn);
}

/**
* Check if user should be admin based on adminPrincipals configuration
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openmetadata.service.jdbi3;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -68,6 +69,8 @@ void testEnsureLdapConfigDefaultValues_AllFieldsNull() {

assertNotNull(ldapConfig.getAuthRolesMapping());
assertEquals("", ldapConfig.getAuthRolesMapping());

assertFalse(Boolean.TRUE.equals(ldapConfig.getRecursiveGroupMembership()));
}

@Test
Expand All @@ -86,6 +89,7 @@ void testEnsureLdapConfigDefaultValues_SomeFieldsNull() {
assertEquals("", ldapConfig.getGroupBaseDN());
assertEquals("", ldapConfig.getAllAttributeName());
assertEquals("", ldapConfig.getAuthRolesMapping());
assertFalse(Boolean.TRUE.equals(ldapConfig.getRecursiveGroupMembership()));
}

@Test
Expand Down Expand Up @@ -143,5 +147,6 @@ void testEnsureLdapConfigDefaultValues_OtherOptionalFieldsDefaults() {
assertEquals("", ldapConfig.getRoleAdminName());
assertEquals("", ldapConfig.getAllAttributeName());
assertEquals("", ldapConfig.getAuthRolesMapping());
assertFalse(Boolean.TRUE.equals(ldapConfig.getRecursiveGroupMembership()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.openmetadata.service.security.auth;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;

import com.unboundid.ldap.sdk.Filter;
import org.junit.jupiter.api.Test;
import org.openmetadata.schema.auth.LdapConfiguration;

class LdapAuthenticatorTest {

@Test
void buildGroupMemberFilterUsesEqualityWhenRecursiveMembershipIsDisabled() {
LdapConfiguration ldapConfiguration = new LdapConfiguration();
ldapConfiguration.setGroupMemberAttributeName("member");
ldapConfiguration.setRecursiveGroupMembership(false);

Filter filter =
LdapAuthenticator.buildGroupMemberFilter(
ldapConfiguration, "cn=john,ou=users,dc=example,dc=com");

assertEquals(Filter.FILTER_TYPE_EQUALITY, filter.getFilterType());
assertEquals("member", filter.getAttributeName());
assertEquals("cn=john,ou=users,dc=example,dc=com", filter.getAssertionValue());
assertFalse(filter.getDNAttributes());
assertNull(filter.getMatchingRuleID());
}

@Test
void buildGroupMemberFilterUsesRecursiveMatchRuleWhenEnabled() {
LdapConfiguration ldapConfiguration = new LdapConfiguration();
ldapConfiguration.setGroupMemberAttributeName("member");
ldapConfiguration.setRecursiveGroupMembership(true);

Filter filter =
LdapAuthenticator.buildGroupMemberFilter(
ldapConfiguration, "cn=john,ou=users,dc=example,dc=com");

assertEquals(Filter.FILTER_TYPE_EXTENSIBLE_MATCH, filter.getFilterType());
assertEquals("member", filter.getAttributeName());
assertEquals("1.2.840.113556.1.4.1941", filter.getMatchingRuleID());
assertEquals("cn=john,ou=users,dc=example,dc=com", filter.getAssertionValue());
assertFalse(filter.getDNAttributes());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
"description": "Group Member Name attribute name",
"type": "string"
},
"recursiveGroupMembership": {
"description": "Enable transitive group membership resolution for Active Directory nested groups using LDAP_MATCHING_RULE_IN_CHAIN.",
"type": "boolean",
"default": false
},
"authRolesMapping": {
"description": "Json string of roles mapping between LDAP roles and Ranger roles",
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ LDAP authentication enables users to log in with their LDAP directory credential
4. Example: `member: cn=john,ou=users,dc=company,dc=com` → use `member`
- **Validation:** OpenMetadata checks this attribute exists on actual group objects

## <span data-id="recursiveGroupMembership">Recursive Group Membership</span>

- **Definition:** Enables nested group resolution for Active Directory.
- **Why it matters:** If an AD group contains another group, users in the nested group will still match the mapped parent group.
- **How it works:** Uses Active Directory's transitive membership matching rule instead of a direct member equality filter.
- **When to enable:** Turn this on only when your LDAP directory is Active Directory and you rely on nested groups for access control.

## <span data-id="authRolesMapping">Auth Roles Mapping</span>

- **Definition:** Mapping between LDAP groups and OpenMetadata roles.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ export const LDAP_UI_SCHEMA = {
groupAttributeName: { 'ui:title': 'Group Attribute Name' },
groupAttributeValue: { 'ui:title': 'Group Attribute Value' },
groupMemberAttributeName: { 'ui:title': 'Group Member Attribute Name' },
recursiveGroupMembership: {
'ui:title': 'Recursive Group Membership',
'ui:help':
'Enable this for Active Directory nested groups. OpenMetadata will use AD transitive membership matching when checking role mappings.',
},
authRolesMapping: {
'ui:title': 'Auth Roles Mapping',
'ui:widget': 'LdapRoleMappingWidget',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ export interface LDAPConfiguration {
* Port of the server
*/
port: number;
/**
* Enable transitive group membership resolution for Active Directory nested groups using
* LDAP_MATCHING_RULE_IN_CHAIN.
*/
recursiveGroupMembership?: boolean;
/**
* Admin role name
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export interface LDAPConfiguration {
* Port of the server
*/
port: number;
/**
* Enable transitive group membership resolution for Active Directory nested groups using
* LDAP_MATCHING_RULE_IN_CHAIN.
*/
recursiveGroupMembership?: boolean;
/**
* Admin role name
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ export interface LDAPConfiguration {
* Port of the server
*/
port: number;
/**
* Enable transitive group membership resolution for Active Directory nested groups using
* LDAP_MATCHING_RULE_IN_CHAIN.
*/
recursiveGroupMembership?: boolean;
/**
* Admin role name
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,11 @@ export interface LDAPConfiguration {
* Port of the server
*/
port: number;
/**
* Enable transitive group membership resolution for Active Directory nested groups using
* LDAP_MATCHING_RULE_IN_CHAIN.
*/
recursiveGroupMembership?: boolean;
/**
* Admin role name
*/
Expand Down
Loading