handavid commented on code in PR #1454:
URL: https://github.com/apache/knox/pull/1454#discussion_r4322448345


##########
gateway-spi/src/main/java/org/apache/knox/gateway/services/ldap/RoleAssignment.java:
##########
@@ -20,6 +20,18 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 public class RoleAssignment {
+    public static RoleAssignment createFromDisplayValue(String displayValue) {
+        String[] tokens = displayValue.split(":");

Review Comment:
   done



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/interceptor/LDAPRolesLookupInterceptor.java:
##########
@@ -86,6 +96,90 @@ public EntryFilteringCursor search(SearchOperationContext 
ctx) throws LdapExcept
             }
         }
 
+        if (isAllGroupsSearch(ctx)) {
+            return handleAllGroupsSearch(ctx);
+        } else {
+            return handleSearch(ctx);
+        }
+    }
+
+    private boolean isAllGroupsSearch(SearchOperationContext ctx) {
+        Dn searchDn = ctx.getDn();
+        if (searchDn == null) {
+            return false;
+        }
+
+        if (ctx.getScope() == SearchScope.OBJECT || 
!isAllGroupsFilter(ctx.getFilter())) {
+            return false;
+        }
+        // Either the proxy groups base, or the root with an explicit group 
filter
+        return searchDn.toString().equalsIgnoreCase(proxyGroupSearchBase) || 
searchDn.size() <= 2;
+    }
+
+    private boolean isAllGroupsFilter(ExprNode filter) {
+        // Case 1: Simple objectClass evaluation (e.g., 
objectClass=groupOfNames)
+        if (filter instanceof EqualityNode) {
+            EqualityNode<?> eqNode = (EqualityNode<?>) filter;
+            if ("objectClass".equalsIgnoreCase(eqNode.getAttribute())) {
+                String value = eqNode.getValue().toString();
+                return "groupOfNames".equalsIgnoreCase(value) ||
+                        "groupOfUniqueNames".equalsIgnoreCase(value);
+            }
+        }
+
+        // Case 2a: Wildcard match on all common names and object classes 
(e.g., cn=*, objectClass=*)
+        if (filter instanceof PresenceNode) {
+            PresenceNode presNode = (PresenceNode) filter;
+            return "cn".equalsIgnoreCase(presNode.getAttribute()) ||
+                    "objectClass".equalsIgnoreCase(presNode.getAttribute());
+        }
+        // Case 2b: Wildcard match on object class (e.g., objectClass=*)
+        if (filter instanceof ObjectClassNode) {
+            return true;
+        }
+
+        // Case 3: Compound filters. An AND is an all groups filter only if 
every child is, an OR
+        // if any child is, e.g. (&(objectClass=groupOfNames)(cn=*)) or 
(|(uid=alice)(cn=*)).
+        // A NOT is never an all groups filter: negating a condition never 
positively selects groups.
+        if (filter instanceof AndNode) {
+            List<ExprNode> children = ((AndNode) filter).getChildren();
+            return !children.isEmpty() && 
children.stream().allMatch(this::isAllGroupsFilter);
+        }
+        if (filter instanceof OrNode) {
+            return ((OrNode) 
filter).getChildren().stream().anyMatch(this::isAllGroupsFilter);
+        }
+
+        return false;
+    }
+
+    private EntryFilteringCursor handleAllGroupsSearch(SearchOperationContext 
ctx) throws LdapException {
+        // short-circuit the LDAP search to return all roles
+        Collection<String> roles = null;
+        try {
+            roles = rolesLookupService.listRoles();
+        } catch (Exception e) {
+            throw new LdapException("Failed to retrieve roles from roles 
lookup service.", e);
+        }
+        List<Entry> entries = new ArrayList<>();
+        for (String role : roles) {
+            Dn roleDn = new Dn(schemaManager, createGroupDnFromName(role));
+            Entry entry = createRoleEntry(roleDn);
+            entries.add(entry);
+        }
+
+        return new EntryFilteringCursorImpl(new ListCursor<>(entries), ctx, 
schemaManager);
+    }
+
+    private EntryFilteringCursor handleSearch(SearchOperationContext ctx) 
throws LdapException {
+        // Replace the role dn with the member dns if the search filter 
contains a "memberOf" node.
+        ExprNode filter = ctx.getFilter();
+        ReverseRoleLookupMappingVisitor reverseRoleLookupMappingVisitor =
+                new ReverseRoleLookupMappingVisitor(schemaManager, 
rolesLookupService, this::createGroupDnFromName);
+        Object updatedFilter = filter.accept(reverseRoleLookupMappingVisitor);

Review Comment:
   done



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/backend/LdapProxyBackend.java:
##########
@@ -576,6 +586,54 @@ public List<Entry> search(String searchBase, SearchScope 
searchScope, String fil
         }
     }
 
+    private String mapMemberOfExpression(String filter, SchemaManager 
schemaManager) throws Exception {
+        if (useMemberOf || !filter.toLowerCase(ROOT).contains("memberof")) {
+            return filter;
+        }
+        // Convert filter if it contains "memberOf" but the backend doesn't 
support "memberOf"
+        MemberOfMappingVisitor visitor = new 
MemberOfMappingVisitor(schemaManager, this::lookupGroup, this::isUser);
+        ExprNode node = FilterParser.parse(schemaManager, filter);

Review Comment:
   moved the filter cleanup to LdapProxyBackend to run before either filter is 
applied



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to