[
https://issues.apache.org/jira/browse/KNOX-3487?focusedWorklogId=1048382&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1048382
]
ASF GitHub Bot logged work on KNOX-3487:
----------------------------------------
Author: ASF GitHub Bot
Created on: 20/Oct/26 07:23
Start Date: 20/Oct/26 07:23
Worklog Time Spent: 10m
Work Description: handavid commented on code in PR #1454:
URL: https://github.com/apache/knox/pull/1454#discussion_r4322446624
##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/backend/LdapProxyBackend.java:
##########
@@ -80,6 +83,8 @@ public class LdapProxyBackend implements LdapBackend {
private static final String[] ALL_ATTRIBUTES = new String[]{"*"};
private static final String[] ALL_ATTRIBUTES_AND_MEMBEROF = new
String[]{"*", "memberOf"};
+ private static final String DEFAULT_MAX_FILTER_LENGTH = "2000";
Review Comment:
default to unlimited
##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/roles/rest/RestApiLdapRolesReverseLookup.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.roles.rest;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.ldap.LdapMessages;
+import org.apache.knox.gateway.services.ldap.RoleAssignees;
+import org.apache.knox.gateway.services.ldap.RoleAssignment;
+import org.apache.knox.gateway.services.ldap.roles.LdapRolesReverseLookup;
+import org.apache.knox.gateway.services.ldap.roles.RoleException;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * REST API based implementation of LdapRolesReverseLookup.
+ */
+public class RestApiLdapRolesReverseLookup implements LdapRolesReverseLookup {
+ private static final LdapMessages LOG =
MessagesFactory.get(LdapMessages.class);
+
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final String listEndpoint;
+ private final String getEndpoint;
+
+ public RestApiLdapRolesReverseLookup(String listEndpoint, String
getEndpoint) {
+ this.listEndpoint = listEndpoint;
+ this.getEndpoint = getEndpoint;
+ }
+
+ @Override
+ public Collection<String> listRoles() throws RoleException {
+ final List<Role> roles = listRoles(null);
+ Set<String> result = new HashSet<>();
+ for (Role role : roles) {
+ result.add(new RoleAssignment(role.getScope(),
role.getName()).getDisplayValue());
+ }
+ return result;
+ }
+
+ @Override
+ public RoleAssignees lookupRoleAssignees(String role) throws RoleException
{
+ if (role != null) {
+ RoleAssignment roleAssignment =
RoleAssignment.createFromDisplayValue(role);
+ List<Role> rolesList = listRoles(roleAssignment);
+ String roleId = findRoleId(roleAssignment, rolesList);
+ if (roleId != null) {
+ List<String> users = new ArrayList<>();
+ List<String> groups = new ArrayList<>();
+ List<RoleMember> members = getMembers(roleId);
+ if (members != null) {
+ for (RoleMember member : members) {
+ if (member.getMemberType().equalsIgnoreCase("user")) {
+ users.add(member.getMemberId());
+ } else if
(member.getMemberType().equalsIgnoreCase("group")) {
+ groups.add(member.getMemberId());
+ } else {
+ // ignore unknown member types
+ LOG.restRolesAssigneeTypeUnknown(role,
member.getMemberType());
+ }
+ }
+ }
+ return new RoleAssignees(users, groups);
+ }
+ }
+ return new RoleAssignees(List.of(), List.of());
+ }
+
+ private List<RoleMember> getMembers(String roleId) throws RoleException {
+ String encodedRoleId = URLEncoder.encode(roleId,
StandardCharsets.UTF_8)
+ .replace("+", "%20"); // URLEncoder converts spaces to '+',
path params need '%20'
+ String getUrl = getEndpoint + "/" + encodedRoleId;
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ LOG.restRolesGetRequest(getUrl, roleId);
+ HttpGet getRequest = new HttpGet(getUrl);
+ getRequest.setHeader("Accept", "application/json");
+ try (CloseableHttpResponse response =
httpClient.execute(getRequest)) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ final String jsonResponse = entity == null ? null :
EntityUtils.toString(entity);
+ LOG.restRolesGetResponse(getUrl, statusCode, jsonResponse);
+
+ if (statusCode != 200) {
+ LOG.restRolesLookupHttpError(getUrl, statusCode,
jsonResponse);
+ throw new RoleException("Failed to get roles: HTTP " +
statusCode);
+ }
+
+ if (jsonResponse == null) {
+ throw new RoleException("Empty response from role get
API");
+ }
+
+ return mapper.readValue(jsonResponse, Role.class).getMembers();
+ }
+ } catch (IOException e) {
+ throw new RoleException("Error while executing role get", e);
+ }
+ }
+
+ private List<Role> listRoles(RoleAssignment roleAssignment) throws
RoleException {
+ String listUrl = null;
+ int numRolesRetrieved = 0;
+ int totalExpectedRoles = 0;
+ List<Role> roles = new ArrayList<>();
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ do {
+ URIBuilder uriBuilder = new URIBuilder(listEndpoint);
+ if (roleAssignment != null) {
+ if (roleAssignment.getName() != null) {
+ uriBuilder.addParameter("name",
roleAssignment.getName());
+ }
+ if (roleAssignment.getScope() != null) {
+ uriBuilder.addParameter("scope",
roleAssignment.getScope());
+ }
+ }
+ uriBuilder.addParameter("offset",
Integer.toString(numRolesRetrieved));
+ URI listURI = uriBuilder.build();
+ listUrl = listURI.toString();
+ LOG.restRolesListRequest(listUrl);
+
+ HttpGet listRequest = new HttpGet(listUrl);
+ listRequest.setHeader("Accept", "application/json");
+ try (CloseableHttpResponse response =
httpClient.execute(listRequest)) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ final String jsonResponse = entity == null ? null :
EntityUtils.toString(entity);
+ LOG.restRolesListResponse(listUrl, statusCode,
jsonResponse);
+
+ if (statusCode != 200) {
+ LOG.restRolesLookupHttpError(listUrl, statusCode,
jsonResponse);
+ throw new RoleException("Failed to list roles: HTTP "
+ statusCode);
+ }
+
+ if (jsonResponse == null) {
+ throw new RoleException("Empty response from role list
API");
+ }
+
+ ListRolesResponse listRolesResponse =
mapper.readValue(jsonResponse, ListRolesResponse.class);
+ numRolesRetrieved += listRolesResponse.getData().size();
Review Comment:
added null-checks
##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/roles/rest/RestApiLdapRolesReverseLookup.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.roles.rest;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.ldap.LdapMessages;
+import org.apache.knox.gateway.services.ldap.RoleAssignees;
+import org.apache.knox.gateway.services.ldap.RoleAssignment;
+import org.apache.knox.gateway.services.ldap.roles.LdapRolesReverseLookup;
+import org.apache.knox.gateway.services.ldap.roles.RoleException;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * REST API based implementation of LdapRolesReverseLookup.
+ */
+public class RestApiLdapRolesReverseLookup implements LdapRolesReverseLookup {
+ private static final LdapMessages LOG =
MessagesFactory.get(LdapMessages.class);
+
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final String listEndpoint;
+ private final String getEndpoint;
+
+ public RestApiLdapRolesReverseLookup(String listEndpoint, String
getEndpoint) {
+ this.listEndpoint = listEndpoint;
+ this.getEndpoint = getEndpoint;
+ }
+
+ @Override
+ public Collection<String> listRoles() throws RoleException {
+ final List<Role> roles = listRoles(null);
+ Set<String> result = new HashSet<>();
+ for (Role role : roles) {
+ result.add(new RoleAssignment(role.getScope(),
role.getName()).getDisplayValue());
+ }
+ return result;
+ }
+
+ @Override
+ public RoleAssignees lookupRoleAssignees(String role) throws RoleException
{
+ if (role != null) {
+ RoleAssignment roleAssignment =
RoleAssignment.createFromDisplayValue(role);
+ List<Role> rolesList = listRoles(roleAssignment);
+ String roleId = findRoleId(roleAssignment, rolesList);
+ if (roleId != null) {
+ List<String> users = new ArrayList<>();
+ List<String> groups = new ArrayList<>();
+ List<RoleMember> members = getMembers(roleId);
+ if (members != null) {
+ for (RoleMember member : members) {
+ if (member.getMemberType().equalsIgnoreCase("user")) {
+ users.add(member.getMemberId());
+ } else if
(member.getMemberType().equalsIgnoreCase("group")) {
+ groups.add(member.getMemberId());
+ } else {
+ // ignore unknown member types
+ LOG.restRolesAssigneeTypeUnknown(role,
member.getMemberType());
+ }
+ }
+ }
+ return new RoleAssignees(users, groups);
+ }
+ }
+ return new RoleAssignees(List.of(), List.of());
+ }
+
+ private List<RoleMember> getMembers(String roleId) throws RoleException {
+ String encodedRoleId = URLEncoder.encode(roleId,
StandardCharsets.UTF_8)
+ .replace("+", "%20"); // URLEncoder converts spaces to '+',
path params need '%20'
+ String getUrl = getEndpoint + "/" + encodedRoleId;
Review Comment:
switched to URIBuilder
##########
gateway-server/src/main/java/org/apache/knox/gateway/services/ldap/roles/rest/RestApiLdapRolesReverseLookup.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.roles.rest;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.ldap.LdapMessages;
+import org.apache.knox.gateway.services.ldap.RoleAssignees;
+import org.apache.knox.gateway.services.ldap.RoleAssignment;
+import org.apache.knox.gateway.services.ldap.roles.LdapRolesReverseLookup;
+import org.apache.knox.gateway.services.ldap.roles.RoleException;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * REST API based implementation of LdapRolesReverseLookup.
+ */
+public class RestApiLdapRolesReverseLookup implements LdapRolesReverseLookup {
+ private static final LdapMessages LOG =
MessagesFactory.get(LdapMessages.class);
+
+ private final ObjectMapper mapper = new ObjectMapper();
+ private final String listEndpoint;
+ private final String getEndpoint;
+
+ public RestApiLdapRolesReverseLookup(String listEndpoint, String
getEndpoint) {
+ this.listEndpoint = listEndpoint;
+ this.getEndpoint = getEndpoint;
+ }
+
+ @Override
+ public Collection<String> listRoles() throws RoleException {
+ final List<Role> roles = listRoles(null);
+ Set<String> result = new HashSet<>();
+ for (Role role : roles) {
+ result.add(new RoleAssignment(role.getScope(),
role.getName()).getDisplayValue());
+ }
+ return result;
+ }
+
+ @Override
+ public RoleAssignees lookupRoleAssignees(String role) throws RoleException
{
+ if (role != null) {
+ RoleAssignment roleAssignment =
RoleAssignment.createFromDisplayValue(role);
+ List<Role> rolesList = listRoles(roleAssignment);
+ String roleId = findRoleId(roleAssignment, rolesList);
+ if (roleId != null) {
+ List<String> users = new ArrayList<>();
+ List<String> groups = new ArrayList<>();
+ List<RoleMember> members = getMembers(roleId);
+ if (members != null) {
+ for (RoleMember member : members) {
+ if (member.getMemberType().equalsIgnoreCase("user")) {
+ users.add(member.getMemberId());
+ } else if
(member.getMemberType().equalsIgnoreCase("group")) {
+ groups.add(member.getMemberId());
+ } else {
+ // ignore unknown member types
+ LOG.restRolesAssigneeTypeUnknown(role,
member.getMemberType());
+ }
+ }
+ }
+ return new RoleAssignees(users, groups);
+ }
+ }
+ return new RoleAssignees(List.of(), List.of());
+ }
+
+ private List<RoleMember> getMembers(String roleId) throws RoleException {
+ String encodedRoleId = URLEncoder.encode(roleId,
StandardCharsets.UTF_8)
+ .replace("+", "%20"); // URLEncoder converts spaces to '+',
path params need '%20'
+ String getUrl = getEndpoint + "/" + encodedRoleId;
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ LOG.restRolesGetRequest(getUrl, roleId);
+ HttpGet getRequest = new HttpGet(getUrl);
+ getRequest.setHeader("Accept", "application/json");
+ try (CloseableHttpResponse response =
httpClient.execute(getRequest)) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ final String jsonResponse = entity == null ? null :
EntityUtils.toString(entity);
+ LOG.restRolesGetResponse(getUrl, statusCode, jsonResponse);
+
+ if (statusCode != 200) {
+ LOG.restRolesLookupHttpError(getUrl, statusCode,
jsonResponse);
Review Comment:
fixed
##########
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);
+ // Baseline in the same form as the visitor output so an unchanged
visit exits on the first pass
+ String current = node.toString();
+ for (int i = 0; i < recursiveGroupResolutionMaxDepth; ++i) {
Review Comment:
done
Issue Time Tracking
-------------------
Worklog Id: (was: 1048382)
Time Spent: 1h 10m (was: 1h)
> LDAP Proxy find all members of a role
> -------------------------------------
>
> Key: KNOX-3487
> URL: https://issues.apache.org/jira/browse/KNOX-3487
> Project: Apache Knox
> Issue Type: Improvement
> Components: Server
> Affects Versions: 3.1.0
> Reporter: David Han
> Assignee: David Han
> Priority: Major
> Fix For: 3.1.0
>
> Time Spent: 1h 10m
> Remaining Estimate: 0h
>
> When role lookup is enabled on the LDAP Proxy, the LDAP Proxy should be able
> to return all users that are assigned a role. E.g.,
> {code}
> ldapsearch -b 'ou=users,DC=proxy,DC=com' -s sub
> '(memberOf=cn=rolename,ou=groups,dc=proxy,dc=com)' '*'
> {code}
> This would require calling the roles lookup service to get the mapped users
> and groups for the given role. Then modifying the search filter to replace
> the rolename with the users and groups.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)