NIFI-14026 Added proxied-entity module for web-security and toolkit

- Added ProxiedEntityEncoder interface and implementation
- Removed nifi-web-security dependency from nifi-toolkit-client
- Removed Google Guava dependency from nifi-toolkit-cli

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes #9567.
This commit is contained in:
exceptionfactory 2024-12-04 11:20:54 -06:00 committed by Pierre Villard
parent df793ce14e
commit 02947e0eee
No known key found for this signature in database
GPG Key ID: F92A93B30C07C6D5
14 changed files with 242 additions and 192 deletions

View File

@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
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.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
<version>2.1.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-security-proxied-entity</artifactId>
<description>Shared security components for handling Proxied Entities in HTTP requests and response</description>
</project>

View File

@ -0,0 +1,29 @@
/*
* 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.nifi.security.proxied.entity;
/**
* Proxied Entity Encoder sanitizes and formats identities for transmission as HTTP request headers
*/
public interface ProxiedEntityEncoder {
/**
* Get encoded representation of identity suitable for transmission
* @param identity Identity to be encoded
* @return Encoded Entity
*/
String getEncodedEntity(String identity);
}

View File

@ -0,0 +1,89 @@
/*
* 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.nifi.security.proxied.entity;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* Standard implementation singleton instance of Proxied Entity Encoder
*/
public class StandardProxiedEntityEncoder implements ProxiedEntityEncoder {
private static final String DELIMITED_FORMAT = "<%s>";
private static final String GT = ">";
private static final String ESCAPED_GT = "\\\\>";
private static final String LT = "<";
private static final String ESCAPED_LT = "\\\\<";
private static final CharsetEncoder headerValueCharsetEncoder = StandardCharsets.US_ASCII.newEncoder();
private static final Base64.Encoder headerValueEncoder = Base64.getEncoder();
private static final StandardProxiedEntityEncoder encoder = new StandardProxiedEntityEncoder();
private StandardProxiedEntityEncoder() {
}
/**
* Get singleton instance of standard encoder
*
* @return Proxied Entity Encoder
*/
public static ProxiedEntityEncoder getInstance() {
return encoder;
}
/**
* Get encoded entity sanitized and formatted for transmission as an HTTP header
*
* @param identity Identity to be encoded
* @return Encoded Entity
*/
@Override
public String getEncodedEntity(final String identity) {
final String sanitizedIdentity = getSanitizedIdentity(identity);
return DELIMITED_FORMAT.formatted(sanitizedIdentity);
}
private String getSanitizedIdentity(final String identity) {
final String sanitized;
if (identity == null || identity.isEmpty()) {
sanitized = identity;
} else {
final String escaped = identity.replaceAll(LT, ESCAPED_LT).replaceAll(GT, ESCAPED_GT);
if (headerValueCharsetEncoder.canEncode(escaped)) {
// Strings limited to US-ASCII characters can be transmitted as HTTP header values without encoding
sanitized = escaped;
} else {
// Non-ASCII characters require Base64 encoding and additional wrapping for transmission as headers
final byte[] escapedBinary = escaped.getBytes(StandardCharsets.UTF_8);
final String escapedEncoded = headerValueEncoder.encodeToString(escapedBinary);
sanitized = DELIMITED_FORMAT.formatted(escapedEncoded);
}
}
return sanitized;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.nifi.security.proxied.entity;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
class StandardProxiedEntityEncoderTest {
@ParameterizedTest
@CsvSource(
delimiter = ':',
value = {
":<null>",
"'':<>",
"username:<username>",
"CN=Common Name, OU=Organizational Unit:<CN=Common Name, OU=Organizational Unit>",
"CN=nifi.apache.org, O=Organization:<CN=nifi.apache.org, O=Organization>",
"<username>:<\\<username\\>>",
"<<username>>:<\\<\\<username\\>\\>>",
"CN=\uD83D\uDE00:<<Q0498J+YgA==>>"
}
)
void testGetEncodedEntity(final String identity, final String expected) {
final String encodedEntity = StandardProxiedEntityEncoder.getInstance().getEncodedEntity(identity);
assertEquals(expected, encodedEntity);
}
}

View File

@ -51,6 +51,7 @@
<module>nifi-security-identity</module>
<module>nifi-security-kerberos-api</module>
<module>nifi-security-kerberos</module>
<module>nifi-security-proxied-entity</module>
<module>nifi-security-ssl</module>
<module>nifi-security-utils-api</module>
<module>nifi-single-user-utils</module>

View File

@ -111,6 +111,11 @@
<artifactId>nifi-security-cert</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-proxied-entity</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>

View File

@ -29,6 +29,8 @@ import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.security.proxied.entity.ProxiedEntityEncoder;
import org.apache.nifi.security.proxied.entity.StandardProxiedEntityEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
@ -51,32 +53,7 @@ public class ProxiedEntitiesUtils {
private static final String ANONYMOUS_CHAIN = "<>";
private static final String ANONYMOUS_IDENTITY = "";
/**
* Formats a list of DN/usernames to be set as a HTTP header using well known conventions.
*
* @param proxiedEntities the raw identities (usernames and DNs) to be formatted as a chain
* @return the value to use in the X-ProxiedEntitiesChain header
*/
public static String getProxiedEntitiesChain(final String... proxiedEntities) {
return getProxiedEntitiesChain(Arrays.asList(proxiedEntities));
}
/**
* Formats a list of DN/usernames to be set as a HTTP header using well known conventions.
*
* @param proxiedEntities the raw identities (usernames and DNs) to be formatted as a chain
* @return the value to use in the X-ProxiedEntitiesChain header
*/
public static String getProxiedEntitiesChain(final List<String> proxiedEntities) {
if (proxiedEntities == null) {
return null;
}
final List<String> proxiedEntityChain = proxiedEntities.stream()
.map(org.apache.nifi.registry.security.util.ProxiedEntitiesUtils::formatProxyDn)
.collect(Collectors.toList());
return StringUtils.join(proxiedEntityChain, "");
}
private static final ProxiedEntityEncoder proxiedEntityEncoder = StandardProxiedEntityEncoder.getInstance();
/**
* Tokenizes the specified proxy chain.
@ -134,15 +111,13 @@ public class ProxiedEntitiesUtils {
if (proxyChain.isEmpty()) {
return ANONYMOUS_CHAIN;
}
proxyChain = proxyChain.stream().map(ProxiedEntitiesUtils::formatProxyDn).collect(Collectors.toList());
proxyChain = proxyChain.stream().map(proxiedEntityEncoder::getEncodedEntity).collect(Collectors.toList());
return StringUtils.join(proxyChain, "");
}
/**
* Builds the string representation for a set of groups that belong to a proxied entity.
*
* The resulting string will be formatted similar to a proxied-entity chain.
*
* Example:
* Groups set: ("group1", "group2", "group3")
* Returns: {@code "<group1><group2><group3> }
@ -155,7 +130,7 @@ public class ProxiedEntitiesUtils {
return PROXY_ENTITY_GROUPS_EMPTY;
}
final List<String> formattedGroups = groups.stream().map(ProxiedEntitiesUtils::formatProxyDn).collect(Collectors.toList());
final List<String> formattedGroups = groups.stream().map(proxiedEntityEncoder::getEncodedEntity).collect(Collectors.toList());
return StringUtils.join(formattedGroups, "");
}
@ -187,68 +162,6 @@ public class ProxiedEntitiesUtils {
}
}
/**
* Formats the specified DN to be set as a HTTP header using well known conventions.
*
* @param dn raw dn
* @return the dn formatted as an HTTP header
*/
public static String formatProxyDn(final String dn) {
return LT + sanitizeDn(dn) + GT;
}
/**
* Sanitizes a DN for safe and lossless transmission.
*
* Sanitization requires:
* <ol>
* <li>Encoded so that it can be sent losslessly using US-ASCII (the character set of HTTP Header values)</li>
* <li>Resilient to a DN with the sequence '><' to attempt to escape the tokenization process and impersonate another user.</li>
* </ol>
*
* <p>
* Example:
* <p>
* Provided DN: {@code jdoe><alopresto} -> {@code <jdoe><alopresto><proxy...>} would allow the user to impersonate jdoe
* <p>Алйс
* Provided DN: {@code Алйс} -> {@code <Алйс>} cannot be encoded/decoded as ASCII
*
* @param rawDn the unsanitized DN
* @return the sanitized DN
*/
private static String sanitizeDn(final String rawDn) {
if (StringUtils.isEmpty(rawDn)) {
return rawDn;
} else {
// First, escape any GT [>] or LT [<] characters, which are not safe
final String escapedDn = rawDn.replaceAll(GT, ESCAPED_GT).replaceAll(LT, ESCAPED_LT);
if (!escapedDn.equals(rawDn)) {
logger.warn("The provided DN [{}] contained dangerous characters that were escaped to [{}]", rawDn, escapedDn);
}
// Second, check for characters outside US-ASCII.
// This is necessary because X509 Certs can contain international/Unicode characters,
// but this value will be passed in an HTTP Header which must be US-ASCII.
// If non-ascii characters are present, base64 encode the DN and wrap in <angled-brackets>,
// to indicate to the receiving end that the value must be decoded.
// Note: We could have decided to always base64 encode these values,
// not only to avoid the isPureAscii(...) check, but also as a
// method of sanitizing GT [>] or LT [<] chars. However, there
// are advantages to encoding only when necessary, namely:
// 1. Backwards compatibility
// 2. Debugging this X-ProxiedEntitiesChain headers is easier unencoded.
// This algorithm can be revisited as part of the next major version change.
if (isPureAscii(escapedDn)) {
return escapedDn;
} else {
final String encodedDn = base64Encode(escapedDn);
logger.debug("The provided DN [{}] contained non-ASCII characters and was encoded as [{}]", rawDn, encodedDn);
return encodedDn;
}
}
}
/**
* Reconstitutes the original DN from the sanitized version passed in the proxy chain.
* <p>
@ -281,19 +194,7 @@ public class ProxiedEntitiesUtils {
}
/**
* Base64 encodes a DN and wraps it in angled brackets to indicate the value is base64 and not a raw DN.
*
* @param rawValue The value to encode
* @return A string containing a wrapped, encoded value.
*/
private static String base64Encode(final String rawValue) {
final String base64String = Base64.getEncoder().encodeToString(rawValue.getBytes(StandardCharsets.UTF_8));
final String wrappedEncodedValue = LT + base64String + GT;
return wrappedEncodedValue;
}
/**
* Performs the reverse of ${@link #base64Encode(String)}.
* Performs the reverse of Base64 encoding
*
* @param encodedValue the encoded value to decode.
* @return The original, decoded string.
@ -314,7 +215,7 @@ public class ProxiedEntitiesUtils {
}
/**
* Check if a value has been encoded by ${@link #base64Encode(String)}, and therefore needs to be decoded.
* Check if a value has been encoded by Base64 encoding and therefore needs to be decoded.
*
* @param token the value to check
* @return true if the value is encoded, false otherwise.
@ -332,14 +233,4 @@ public class ProxiedEntitiesUtils {
private static boolean isWrappedInAngleBrackets(final String string) {
return string.startsWith(LT) && string.endsWith(GT);
}
/**
* Check if a string contains only pure ascii characters.
*
* @param stringWithUnknownCharacters - the string to check
* @return true if string can be encoded as ascii. false otherwise.
*/
private static boolean isPureAscii(final String stringWithUnknownCharacters) {
return StandardCharsets.US_ASCII.newEncoder().canEncode(stringWithUnknownCharacters);
}
}

View File

@ -19,8 +19,6 @@ package org.apache.nifi.web.security;
import org.apache.nifi.authorization.user.NiFiUser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ -33,7 +31,6 @@ import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@ -45,7 +42,6 @@ public class ProxiedEntitiesUtilsTest {
private static final String SAFE_USER_NAME_PROXY_2 = "proxy2.nifi.apache.org";
private static final String SAFE_USER_DN_PROXY_2 = "CN=" + SAFE_USER_NAME_PROXY_2 + ", OU=Apache NiFi";
private static final String MALICIOUS_USER_NAME_JOHN = SAFE_USER_NAME_JOHN + ", OU=Apache NiFi><CN=" + SAFE_USER_NAME_PROXY_1;
private static final String MALICIOUS_USER_DN_JOHN = "CN=" + MALICIOUS_USER_NAME_JOHN + ", OU=Apache NiFi";
private static final String MALICIOUS_USER_NAME_JOHN_ESCAPED = sanitizeDn(MALICIOUS_USER_NAME_JOHN);
private static final String UNICODE_DN_1 = "CN=Алйс, OU=Apache NiFi";
private static final String UNICODE_DN_1_ENCODED = "<" + base64Encode(UNICODE_DN_1) + ">";
@ -62,57 +58,6 @@ public class ProxiedEntitiesUtilsTest {
return Base64.getEncoder().encodeToString(dn.getBytes(StandardCharsets.UTF_8));
}
@ParameterizedTest
@MethodSource("getMaliciousNames" )
public void testSanitizeDnShouldHandleFuzzing(String maliciousName) {
assertNotEquals(formatDn(SAFE_USER_NAME_JOHN), ProxiedEntitiesUtils.formatProxyDn(maliciousName));
}
// Contains various attempted >< escapes, trailing NULL, and BACKSPACE + 'n'
private static List<String> getMaliciousNames() {
return Arrays.asList(MALICIOUS_USER_NAME_JOHN,
SAFE_USER_NAME_JOHN + ">",
SAFE_USER_NAME_JOHN + "><>",
SAFE_USER_NAME_JOHN + "\\>",
SAFE_USER_NAME_JOHN + "\u003e",
SAFE_USER_NAME_JOHN + "\u005c\u005c\u003e",
SAFE_USER_NAME_JOHN + "\u0000",
SAFE_USER_NAME_JOHN + "\u0008n");
}
@Test
public void testShouldFormatProxyDn() {
assertEquals(formatDn(SAFE_USER_DN_JOHN), ProxiedEntitiesUtils.formatProxyDn(SAFE_USER_DN_JOHN));
}
@Test
public void testFormatProxyDnShouldHandleMaliciousInput() {
assertEquals(formatSanitizedDn(MALICIOUS_USER_DN_JOHN), ProxiedEntitiesUtils.formatProxyDn(MALICIOUS_USER_DN_JOHN));
}
@Test
public void testGetProxiedEntitiesChain() {
String[] input = new String[] {SAFE_USER_NAME_JOHN, SAFE_USER_DN_PROXY_1, SAFE_USER_DN_PROXY_2};
assertEquals(formatDns(input), ProxiedEntitiesUtils.getProxiedEntitiesChain(input));
}
@Test
public void testGetProxiedEntitiesChainShouldHandleMaliciousInput() {
final String expectedOutput = formatSanitizedDn(MALICIOUS_USER_DN_JOHN) + formatDns(SAFE_USER_DN_PROXY_1, SAFE_USER_DN_PROXY_2);
assertEquals(expectedOutput, ProxiedEntitiesUtils.getProxiedEntitiesChain(MALICIOUS_USER_DN_JOHN, SAFE_USER_DN_PROXY_1, SAFE_USER_DN_PROXY_2));
}
@Test
public void testGetProxiedEntitiesChainShouldEncodeUnicode() {
assertEquals(formatDns(SAFE_USER_NAME_JOHN, UNICODE_DN_1_ENCODED, UNICODE_DN_2_ENCODED),
ProxiedEntitiesUtils.getProxiedEntitiesChain(SAFE_USER_NAME_JOHN, UNICODE_DN_1, UNICODE_DN_2));
}
@Test
public void testFormatProxyDnShouldEncodeNonAsciiCharacters() {
assertEquals(formatDn(UNICODE_DN_1_ENCODED), ProxiedEntitiesUtils.formatProxyDn(UNICODE_DN_1));
}
@Test
public void testShouldBuildProxyChain(@Mock NiFiUser proxy1, @Mock NiFiUser john) {
when(proxy1.getIdentity()).thenReturn(SAFE_USER_NAME_PROXY_1);

View File

@ -24,7 +24,6 @@
<description>Tooling to make tls configuration easier</description>
<properties>
<guava.version>33.3.1-jre</guava.version>
<jline.version>3.27.1</jline.version>
</properties>
@ -85,12 +84,6 @@
<artifactId>nifi-registry-client</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<!-- Override Guava 31.1 through nifi-framework-core -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-kerberos</artifactId>

View File

@ -21,7 +21,6 @@ import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ComparisonChain;
import org.apache.nifi.flow.VersionedFlowCoordinates;
import org.apache.nifi.flow.VersionedProcessGroup;
import org.apache.nifi.registry.bucket.Bucket;
@ -47,6 +46,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -116,11 +116,9 @@ public class ImportAllFlows extends AbstractNiFiRegistryCommand<StringResult> {
final List<VersionFileMetaData> files = getFilePathList(properties);
// As we need to keep the version order the list needs to be sorted
files.sort((o1, o2) -> ComparisonChain.start()
.compare(o1.getBucketName(), o2.getBucketName())
.compare(o1.getFlowName(), o2.getFlowName())
.compare(o1.getVersion(), o2.getVersion())
.result());
files.sort(Comparator.comparing(VersionFileMetaData::getBucketName)
.thenComparing(VersionFileMetaData::getFlowName)
.thenComparing(VersionFileMetaData::getVersion));
for (VersionFileMetaData file : files) {
final String inputSource = file.getInputSource();

View File

@ -23,7 +23,22 @@ language governing permissions and limitations under the License. -->
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-security</artifactId>
<artifactId>nifi-security-utils-api</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-ssl</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-security-utils</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-proxied-entity</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
@ -36,6 +51,18 @@ language governing permissions and limitations under the License. -->
<artifactId>nifi-client-dto</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>

View File

@ -20,11 +20,11 @@ import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Form;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.toolkit.client.AccessClient;
import org.apache.nifi.toolkit.client.NiFiClientException;
import org.apache.nifi.toolkit.client.RequestConfig;
import org.apache.nifi.toolkit.client.impl.request.BearerTokenRequestConfig;
import org.apache.nifi.util.StringUtils;
import java.io.IOException;
import java.util.Map;

View File

@ -19,10 +19,10 @@ package org.apache.nifi.toolkit.client.impl;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.toolkit.client.NiFiClientException;
import org.apache.nifi.toolkit.client.RequestConfig;
import org.apache.nifi.toolkit.client.TenantsClient;
import org.apache.nifi.util.StringUtils;
import org.apache.nifi.web.api.entity.UserEntity;
import org.apache.nifi.web.api.entity.UserGroupEntity;
import org.apache.nifi.web.api.entity.UserGroupsEntity;

View File

@ -17,8 +17,8 @@
package org.apache.nifi.toolkit.client.impl.request;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.security.proxied.entity.StandardProxiedEntityEncoder;
import org.apache.nifi.toolkit.client.RequestConfig;
import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import java.util.Arrays;
import java.util.HashMap;
@ -33,6 +33,8 @@ import java.util.stream.Collectors;
*/
public class ProxiedEntityRequestConfig implements RequestConfig {
private static final String PROXIED_ENTITIES_CHAIN_HEADER = "X-Proxied-Entities-Chain";
private final String[] proxiedEntities;
public ProxiedEntityRequestConfig(final String... proxiedEntities) {
@ -41,22 +43,22 @@ public class ProxiedEntityRequestConfig implements RequestConfig {
@Override
public Map<String, String> getHeaders() {
final String proxiedEntitiesValue = getProxiedEntitesValue(proxiedEntities);
final String proxiedEntitiesValue = getProxiedEntitiesValue(proxiedEntities);
final Map<String, String> headers = new HashMap<>();
if (proxiedEntitiesValue != null) {
headers.put(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxiedEntitiesValue);
headers.put(PROXIED_ENTITIES_CHAIN_HEADER, proxiedEntitiesValue);
}
return headers;
}
private String getProxiedEntitesValue(final String[] proxiedEntities) {
private String getProxiedEntitiesValue(final String[] proxiedEntities) {
if (proxiedEntities == null) {
return null;
}
final List<String> proxiedEntityChain = Arrays.stream(proxiedEntities)
.map(ProxiedEntitiesUtils::formatProxyDn)
.map(StandardProxiedEntityEncoder.getInstance()::getEncodedEntity)
.collect(Collectors.toList());
return StringUtils.join(proxiedEntityChain, "");
}