[
https://issues.apache.org/jira/browse/KNOX-3247?focusedWorklogId=1004484&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1004484
]
ASF GitHub Bot logged work on KNOX-3247:
----------------------------------------
Author: ASF GitHub Bot
Created on: 11/Feb/26 00:00
Start Date: 11/Feb/26 00:00
Worklog Time Spent: 10m
Work Description: lmccay commented on code in PR #1144:
URL: https://github.com/apache/knox/pull/1144#discussion_r2790838539
##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/backend/LdapProxyBackend.java:
##########
@@ -0,0 +1,377 @@
+/*
+ * 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.backend;
+
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.EntryCursor;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.ldap.LdapMessages;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * LDAP backend that proxies to an external LDAP server.
+ * Can use central LDAP configuration or backend-specific configuration.
+ */
+public class LdapProxyBackend implements LdapBackend {
+ private static final LdapMessages LOG =
MessagesFactory.get(LdapMessages.class);
+
+ private String ldapUrl;
+ private String bindDn;
+ private String bindPassword;
+ private String userSearchBase;
+ private String groupSearchBase;
+ private String proxyBaseDn; // Base DN for proxy entries (e.g.,
dc=proxy,dc=com)
+ private String remoteBaseDn; // Base DN for remote server searches (e.g.,
dc=hadoop,dc=apache,dc=org)
+ private int port;
+ private String host;
+
+ // Configurable attributes for AD/LDAP compatibility
+ private String userIdentifierAttribute = "uid"; // uid for LDAP,
sAMAccountName for AD
+ private String userSearchFilter = "({userIdAttr}={username})"; // Will be
populated with userIdentifierAttribute
+ private String groupMemberAttribute = "memberUid"; // member for AD,
memberUid for POSIX
+ private boolean useMemberOf; // Use memberOf attribute for group lookup
(efficient for AD)
+
+ @Override
+ public String getName() {
+ return "ldap";
+ }
+
+ @Override
+ public void initialize(Map<String, String> config) throws Exception {
+ // Proxy base DN is for entries created in the proxy LDAP server
+ proxyBaseDn = config.get("baseDn");
+ if (proxyBaseDn == null || proxyBaseDn.isEmpty()) {
+ throw new IllegalArgumentException("baseDn is required for LDAP
proxy backend");
+ }
+
+ // Remote base DN is for searching the remote LDAP server
+ remoteBaseDn = config.get("remoteBaseDn");
+ if (remoteBaseDn == null || remoteBaseDn.isEmpty()) {
+ throw new IllegalArgumentException("remoteBaseDn is required for
LDAP proxy backend - this is the base DN of the remote LDAP server");
+ }
+
+ // Support both url and host/port configuration
+ ldapUrl = config.get("url");
+ if (ldapUrl != null && !ldapUrl.isEmpty()) {
+ // Parse URL to extract host and port
+ parseLdapUrl(ldapUrl);
+ } else {
+ host = config.get("host");
+ if (host == null || host.isEmpty()) {
+ throw new IllegalArgumentException("Either 'url' or 'host' is
required for LDAP proxy backend");
+ }
+ String portStr = config.get("port");
+ if (portStr == null || portStr.isEmpty()) {
+ throw new IllegalArgumentException("'port' is required when
using 'host' configuration");
+ }
+ port = Integer.parseInt(portStr);
+ ldapUrl = "ldap://" + host + ":" + port;
+ }
+
+ // Support both naming conventions: bindDn/bindPassword and
systemUsername/systemPassword
+ bindDn = config.get("bindDn");
+ if (bindDn == null || bindDn.isEmpty()) {
+ bindDn = config.get("systemUsername");
+ }
+
+ bindPassword = config.get("bindPassword");
+ if (bindPassword == null || bindPassword.isEmpty()) {
+ bindPassword = config.get("systemPassword");
+ }
+
+ // Search bases use the remote server's base DN
+ userSearchBase = config.getOrDefault("userSearchBase", "ou=people," +
remoteBaseDn);
+ groupSearchBase = config.getOrDefault("groupSearchBase", "ou=groups,"
+ remoteBaseDn);
+
+ // Configure attribute mappings for AD/LDAP compatibility
+ userIdentifierAttribute =
config.getOrDefault("userIdentifierAttribute", "uid");
+ config.getOrDefault("userDnTemplate",
"uid={username},ou=Users,{baseDn}");
+ groupMemberAttribute = config.getOrDefault("groupMemberAttribute",
"memberUid");
+ useMemberOf = Boolean.parseBoolean(config.getOrDefault("useMemberOf",
"false"));
+
+ // Build search filter template
+ userSearchFilter = "(" + userIdentifierAttribute + "={username})";
+
+ LOG.ldapBackendLoading(getName(), "Proxying " + proxyBaseDn + " to " +
ldapUrl + " (" + remoteBaseDn + ") with " +
+ userIdentifierAttribute + " attribute" +
+ (useMemberOf ? " using memberOf lookups" : "
using group searches"));
+ }
+
+ private void parseLdapUrl(String url) {
+ // Simple URL parsing for ldap://host:port
+ if (url.startsWith("ldap://")) {
+ String hostPort = url.substring(7);
+ int colonIdx = hostPort.indexOf(':');
+ if (colonIdx > 0) {
+ host = hostPort.substring(0, colonIdx);
+ try {
+ port = Integer.parseInt(hostPort.substring(colonIdx + 1));
+ } catch (NumberFormatException e) {
+ port = 389;
+ }
+ } else {
+ host = hostPort;
+ port = 389;
+ }
+ } else if (url.startsWith("ldaps://")) {
+ String hostPort = url.substring(8);
+ int colonIdx = hostPort.indexOf(':');
+ if (colonIdx > 0) {
+ host = hostPort.substring(0, colonIdx);
+ try {
+ port = Integer.parseInt(hostPort.substring(colonIdx + 1));
+ } catch (NumberFormatException e) {
+ port = 636;
+ }
+ } else {
+ host = hostPort;
+ port = 636;
+ }
+ }
+ }
+
+ private LdapConnection connect() throws LdapException, IOException {
+ LdapConnection connection = new LdapNetworkConnection(host, port);
Review Comment:
Done - good call.
Issue Time Tracking
-------------------
Worklog Id: (was: 1004484)
Time Spent: 1h (was: 50m)
> Knox LDAP Server with Pluggable Backend
> ---------------------------------------
>
> Key: KNOX-3247
> URL: https://issues.apache.org/jira/browse/KNOX-3247
> Project: Apache Knox
> Issue Type: New Feature
> Components: Server
> Reporter: Larry McCay
> Assignee: Larry McCay
> Priority: Major
> Time Spent: 1h
> Remaining Estimate: 0h
>
> By exposing an LDAP interface from Knox, we can provide a rich set of backend
> implementations that can:
> * Provide easy demo and test environments with a file based backend
> * Provide enterprise integrations by proxying actual LDAP backends
> * Provide novel implementations based on the KNOX-AUTH-SERVICE in other
> topologies
> * All while simplifying the configuration of consumers by normalizing the
> exposed schema - resulting in the same LDAP config for all deployments rather
> than chasing the deployment specific details across the platform.
> Knox can be the central LDAP Server for the platform while integrating with
> all of the possible combinations that we already support.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)