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>
 
-    <!-- LDAP backend proxy configuration (active when 
gateway.ldap.backend.type=ldap) -->
+    <!-- LDAP backend proxy configuration (active when 
gateway.ldap.interceptor.names includes ldapproxy) -->
+    <property>
+        <name>gateway.ldap.interceptor.ldapproxy.backendType</name>
+        <value>ldap</value>
+        <description>Backend type for LDAP service. Currently supported: file, 
ldap. Future: jdbc, knox.</description>
+    </property>
     <property>
-        <name>gateway.ldap.backend.ldap.url</name>
+        <name>gateway.ldap.interceptor.ldapproxy.url</name>
         <value>ldap://localhost:33389</value>
         <description>LDAP server URL for proxy backend</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.remoteBaseDn</name>
+        <name>gateway.ldap.interceptor.ldapproxy.remoteBaseDn</name>
         <value>dc=hadoop,dc=apache,dc=org</value>
         <description>Base DN of the remote LDAP server</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.systemUsername</name>
+        <name>gateway.ldap.interceptor.ldapproxy.systemUsername</name>
         <value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
         <description>LDAP bind DN for proxy backend 
authentication</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.systemPassword</name>
+        <name>gateway.ldap.interceptor.ldapproxy.systemPassword</name>
         <value>guest-password</value>
         <description>LDAP bind password for proxy backend 
authentication</description>
     </property>
 
     <!-- Backend-specific configuration using prefixed properties -->
-    <!-- Uncomment and configure based on backend type specified in 
gateway.ldap.backend.type -->
+    <!-- Uncomment and configure based on interceptor names specified in 
gateway.ldap.interceptor.names -->
 
-    <!-- File backend configuration (gateway.ldap.backend.type=file) -->
+    <!-- File backend configuration 
(gateway.ldap.interceptor.<interceptorName>.backendType=file) -->
     <!--
     <property>
-        <name>gateway.ldap.backend.file.dataFile</name>
+        <name>gateway.ldap.interceptor.ldapfile.backendType</name>
+        <value>ldap</value>
+        <description>Backend type for LDAP service. Currently supported: file, 
ldap. Future: jdbc, knox.</description>
+    </property>
+    <property>
+        <name>gateway.ldap.interceptor.ldapfile.dataFile</name>
         <value>${GATEWAY_DATA_HOME}/ldap-users.json</value>
         <description>Path to JSON file containing user and group 
data</description>
     </property>
     -->
 
-    <!-- LDAP proxy backend configuration (gateway.ldap.backend.type=ldap) -->
+    <!-- LDAP proxy backend configuration 
(gateway.ldap.interceptor.<interceptorName>.backendType=ldap) -->
     <!-- This backend proxies to an external LDAP server (e.g., demo LDAP) -->
     <!--
     Example 1: Using Knox demo LDAP server (default port 33389)

Review Comment:
   I'm not sure I like the idea of putting all these samples in the 
gateway-site.xml here.
   We should rather create the new section in our user guide (see the 
`knox-site` module), and add all these sample configs there.



##########
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>
 
-    <!-- LDAP backend proxy configuration (active when 
gateway.ldap.backend.type=ldap) -->
+    <!-- LDAP backend proxy configuration (active when 
gateway.ldap.interceptor.names includes ldapproxy) -->
+    <property>
+        <name>gateway.ldap.interceptor.ldapproxy.backendType</name>
+        <value>ldap</value>
+        <description>Backend type for LDAP service. Currently supported: file, 
ldap. Future: jdbc, knox.</description>
+    </property>
     <property>
-        <name>gateway.ldap.backend.ldap.url</name>
+        <name>gateway.ldap.interceptor.ldapproxy.url</name>
         <value>ldap://localhost:33389</value>
         <description>LDAP server URL for proxy backend</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.remoteBaseDn</name>
+        <name>gateway.ldap.interceptor.ldapproxy.remoteBaseDn</name>
         <value>dc=hadoop,dc=apache,dc=org</value>
         <description>Base DN of the remote LDAP server</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.systemUsername</name>
+        <name>gateway.ldap.interceptor.ldapproxy.systemUsername</name>
         <value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
         <description>LDAP bind DN for proxy backend 
authentication</description>
     </property>
     <property>
-        <name>gateway.ldap.backend.ldap.systemPassword</name>
+        <name>gateway.ldap.interceptor.ldapproxy.systemPassword</name>
         <value>guest-password</value>
         <description>LDAP bind password for proxy backend 
authentication</description>
     </property>
 
     <!-- Backend-specific configuration using prefixed properties -->
-    <!-- Uncomment and configure based on backend type specified in 
gateway.ldap.backend.type -->
+    <!-- Uncomment and configure based on interceptor names specified in 
gateway.ldap.interceptor.names -->
 
-    <!-- File backend configuration (gateway.ldap.backend.type=file) -->
+    <!-- File backend configuration 
(gateway.ldap.interceptor.<interceptorName>.backendType=file) -->
     <!--
     <property>
-        <name>gateway.ldap.backend.file.dataFile</name>
+        <name>gateway.ldap.interceptor.ldapfile.backendType</name>
+        <value>ldap</value>

Review Comment:
   Should be `file`.



-- 
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