[ 
https://issues.apache.org/jira/browse/KNOX-3330?focusedWorklogId=1023391&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1023391
 ]

ASF GitHub Bot logged work on KNOX-3330:
----------------------------------------

                Author: ASF GitHub Bot
            Created on: 02/Jun/26 20:00
            Start Date: 02/Jun/26 20:00
    Worklog Time Spent: 10m 
      Work Description: smolnar82 commented on code in PR #1240:
URL: https://github.com/apache/knox/pull/1240#discussion_r3343894688


##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/interceptor/UserSearchInterceptor.java:
##########
@@ -98,47 +107,55 @@ public EntryFilteringCursor search(SearchOperationContext 
ctx) throws LdapExcept
                 }
                 originalResults.close();
             } catch (Exception e) {
-                // If we get an error or no results, try the backend
+                // If we get an error or no results, try the backends
             }
 
-            // If no local results, try backend
-            if (entries.isEmpty() && username != null) {
+            if (username != null) {
                 try {
-                    SchemaManager schemaManager = 
directoryService.getSchemaManager();
-
                     if (username.contains("*")) {
                         // Wildcard search - use searchUsers
                         LOG.ldapSearch(baseDn, "wildcard user search: " + 
username);
-                        List<Entry> backendEntries = 
backend.searchUsers(username, schemaManager);
-
                         // Return backend results directly without caching to 
avoid deadlock
                         // (caching during an active search can cause ApacheDS 
locking issues)
-                        entries.addAll(backendEntries);
+                        entries.addAll(searchUsers(username));
                     } else {
-                        // Specific user lookup
-                        LOG.ldapUserLoaded(username);
-                        Entry backendEntry = backend.getUser(username, 
schemaManager);
-
-                        if (backendEntry != null) {
-                            // Return backend result directly without caching
-                            entries.add(backendEntry);
-                            LOG.ldapUserEntry(backendEntry.toString());
-                        } else {
-                            LOG.ldapUserNull(username);
+                        // if no results, perform single-user search
+                        if (entries.isEmpty()) {
+                            // Specific user lookup
+                            LOG.ldapUserLoaded(username);
+                            Entry backendEntry = getUser(username);
+
+                            if (backendEntry != null) {
+                                // Return backend result directly without 
caching
+                                entries.add(backendEntry);
+                                LOG.ldapUserEntry(backendEntry.toString());
+                            } else {
+                                LOG.ldapUserNull(username);
+                            }
                         }
                     }
                 } catch (Exception e) {
-                    LOG.ldapServiceStopFailed(e);
+                    LOG.ldapSearchFailed(baseDn, filter, e);
                 }
             }
 
             // Return cursor with our results - use a simple approach
-            return new EntryFilteringCursorImpl(new ListCursor<>(entries), 
ctx, directoryService.getSchemaManager());
+            return new EntryFilteringCursorImpl(new ListCursor<>(entries), 
ctx, schemaManager);
         }
 
         return originalResults;
     }
 
+    private List<Entry> searchUsers(String userSearchString) throws Exception {
+        List<Entry> entries = new ArrayList<>();
+        entries.addAll(backend.searchUsers(userSearchString, schemaManager));
+        return entries;

Review Comment:
   Why not simply: `return backend.searchUsers(userSearchString, 
schemaManager)`?
   In fact, if this is a simple inline backend invocation, why we have this 
private method at all? No any particular check, logic, etc...in here.



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/interceptor/UserSearchInterceptor.java:
##########
@@ -98,47 +107,55 @@ public EntryFilteringCursor search(SearchOperationContext 
ctx) throws LdapExcept
                 }
                 originalResults.close();
             } catch (Exception e) {
-                // If we get an error or no results, try the backend
+                // If we get an error or no results, try the backends
             }
 
-            // If no local results, try backend
-            if (entries.isEmpty() && username != null) {
+            if (username != null) {
                 try {
-                    SchemaManager schemaManager = 
directoryService.getSchemaManager();
-
                     if (username.contains("*")) {
                         // Wildcard search - use searchUsers
                         LOG.ldapSearch(baseDn, "wildcard user search: " + 
username);
-                        List<Entry> backendEntries = 
backend.searchUsers(username, schemaManager);
-
                         // Return backend results directly without caching to 
avoid deadlock
                         // (caching during an active search can cause ApacheDS 
locking issues)
-                        entries.addAll(backendEntries);
+                        entries.addAll(searchUsers(username));
                     } else {
-                        // Specific user lookup
-                        LOG.ldapUserLoaded(username);
-                        Entry backendEntry = backend.getUser(username, 
schemaManager);
-
-                        if (backendEntry != null) {
-                            // Return backend result directly without caching
-                            entries.add(backendEntry);
-                            LOG.ldapUserEntry(backendEntry.toString());
-                        } else {
-                            LOG.ldapUserNull(username);
+                        // if no results, perform single-user search
+                        if (entries.isEmpty()) {
+                            // Specific user lookup
+                            LOG.ldapUserLoaded(username);
+                            Entry backendEntry = getUser(username);
+
+                            if (backendEntry != null) {
+                                // Return backend result directly without 
caching
+                                entries.add(backendEntry);
+                                LOG.ldapUserEntry(backendEntry.toString());
+                            } else {
+                                LOG.ldapUserNull(username);
+                            }
                         }
                     }
                 } catch (Exception e) {
-                    LOG.ldapServiceStopFailed(e);
+                    LOG.ldapSearchFailed(baseDn, filter, e);
                 }
             }
 
             // Return cursor with our results - use a simple approach
-            return new EntryFilteringCursorImpl(new ListCursor<>(entries), 
ctx, directoryService.getSchemaManager());
+            return new EntryFilteringCursorImpl(new ListCursor<>(entries), 
ctx, schemaManager);
         }
 
         return originalResults;
     }
 
+    private List<Entry> searchUsers(String userSearchString) throws Exception {
+        List<Entry> entries = new ArrayList<>();
+        entries.addAll(backend.searchUsers(userSearchString, schemaManager));
+        return entries;
+    }
+
+    private Entry getUser(String username) throws Exception {
+        return backend.getUser(username, schemaManager);

Review Comment:
   The same as above: I don't think this private method is necessary.



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/interceptor/UserSearchInterceptorFactory.java:
##########
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.services.ldap.interceptor;
+
+import org.apache.directory.server.core.api.interceptor.Interceptor;
+
+import java.util.Map;
+
+public class UserSearchInterceptorFactory implements 
KnoxLdapInterceptorFactory {
+    @Override
+    public Interceptor create(String name, Map<String, String> config) throws 
Exception {
+        return new UserSearchInterceptor(name, config);
+    }
+
+    @Override
+    public String getType() {
+        return "backend";

Review Comment:
   nit: Should be a constant (we could reuse it in KnoxLdapManager, see my 
comment about not using the `instanceof` operator below).



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/KnoxLDAPServerManager.java:
##########
@@ -174,11 +183,30 @@ private void addGroupLookupInterceptor() {
             }
         }
 
-        final GroupLookupInterceptor interceptor = new 
GroupLookupInterceptor(directoryService, backend);
-        if (authIdx != -1) {
-            interceptors.add(authIdx, interceptor);
-        } else {
-            interceptors.add(interceptor);
+        // Add our configured interceptors
+        SchemaManager schemaManager = directoryService.getSchemaManager();
+        for (Interceptor interceptor : interceptors) {
+            if (interceptor instanceof UserSearchInterceptor) {
+                // Create partition for remote base DN if different from proxy 
base DN
+                // This allows backend entries with remote DNs to be returned 
in search results
+                LdapBackend backend = ((UserSearchInterceptor) 
interceptor).getBackend();
+                String remoteBaseDn = backend.getBaseDn();
+                if (!baseDns.contains(remoteBaseDn)) {
+                    //create partition
+                    String id = backend.getName().replaceAll("\\s+", "");
+                    JdbmPartition remotePartition = new 
JdbmPartition(schemaManager, directoryService.getDnFactory());
+                    remotePartition.setId(id);
+                    remotePartition.setSuffixDn(new Dn(schemaManager, 
remoteBaseDn));
+                    remotePartition.setPartitionPath(new File(workDir, 
id).toURI());
+                    directoryService.addPartition(remotePartition);
+                    baseDns.add(remoteBaseDn);
+                }

Review Comment:
   nit: a new private method (`addRemotePartition`, for instance) would 
increase the readability of this part of the code.



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/KnoxLDAPServerManager.java:
##########
@@ -174,11 +183,30 @@ private void addGroupLookupInterceptor() {
             }
         }
 
-        final GroupLookupInterceptor interceptor = new 
GroupLookupInterceptor(directoryService, backend);
-        if (authIdx != -1) {
-            interceptors.add(authIdx, interceptor);
-        } else {
-            interceptors.add(interceptor);
+        // Add our configured interceptors
+        SchemaManager schemaManager = directoryService.getSchemaManager();
+        for (Interceptor interceptor : interceptors) {
+            if (interceptor instanceof UserSearchInterceptor) {

Review Comment:
   I personally am not a big fan of the `instanceof` operator, so this feel 
free to ignore this comment :) 
   
   It might be an option to store the interceptors above in a `Map<String, 
Interceptor>`, where the key is the interceptor type. If that was the case, we 
could simply check if the Map.Entry.key.equals("backend") (this is where my 
comment about creating the `backend` interceptor type constant would be useful).



##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/KnoxLDAPServerManager.java:
##########
@@ -32,27 +34,34 @@
 import org.apache.directory.server.protocol.shared.transport.TcpTransport;
 import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
-import org.apache.knox.gateway.services.ldap.backend.BackendFactory;
 import org.apache.knox.gateway.services.ldap.backend.LdapBackend;
+import org.apache.knox.gateway.services.ldap.interceptor.InterceptorFactory;
+import org.apache.knox.gateway.services.ldap.interceptor.UserSearchInterceptor;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Manages the ApacheDS LDAP server instance with pluggable backends
  */
 public class KnoxLDAPServerManager {
     private static final LdapMessages LOG = 
MessagesFactory.get(LdapMessages.class);
 
-    private DirectoryService directoryService;
+    @VisibleForTesting
+    DirectoryService directoryService;
     private LdapServer ldapServer;
-    private LdapBackend backend;
+    private List<Interceptor> interceptors = new ArrayList<>();

Review Comment:
   This is redundant, as you initialize interceptors anyway (i.e. not simply 
add entries into it).
   See new line 80: `this.interceptors = createInterceptors(config)`



##########
gateway-server/src/main/resources/conf/gateway-site.xml:
##########
@@ -68,61 +68,76 @@ limitations under the License.
         <description>Path to JSON data file for file-based backend. Supports 
${GATEWAY_DATA_HOME} variable.</description>
     </property>
 
-    <!

Issue Time Tracking
-------------------

    Worklog Id:     (was: 1023391)
    Time Spent: 1h  (was: 50m)

> Improve LDAP Proxy configurability for multiple LDAP backends
> -------------------------------------------------------------
>
>                 Key: KNOX-3330
>                 URL: https://issues.apache.org/jira/browse/KNOX-3330
>             Project: Apache Knox
>          Issue Type: Improvement
>          Components: Server
>            Reporter: David Han
>            Assignee: David Han
>            Priority: Major
>          Time Spent: 1h
>  Remaining Estimate: 0h
>
> The Knox ldap proxy uses an interceptor to proxy search requests to a 
> configured backend. This configuration is somewhat limiting in how the proxy 
> can transform search results. Refactor to configure on the interceptor level 
> instead of the backend level so that new types of interceptors can be easily 
> added and multiple backends can be included.
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to