mirror of https://github.com/apache/nifi.git
NIFI-5135:
- Adding support for applying transforms to user identities and group names. This closes #2673. Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
parent
ff04f8efee
commit
5cfa29e48f
|
@ -151,6 +151,10 @@ public abstract class NiFiProperties {
|
|||
public static final String SECURITY_OCSP_RESPONDER_CERTIFICATE = "nifi.security.ocsp.responder.certificate";
|
||||
public static final String SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX = "nifi.security.identity.mapping.pattern.";
|
||||
public static final String SECURITY_IDENTITY_MAPPING_VALUE_PREFIX = "nifi.security.identity.mapping.value.";
|
||||
public static final String SECURITY_IDENTITY_MAPPING_TRANSFORM_PREFIX = "nifi.security.identity.mapping.transform.";
|
||||
public static final String SECURITY_GROUP_MAPPING_PATTERN_PREFIX = "nifi.security.group.mapping.pattern.";
|
||||
public static final String SECURITY_GROUP_MAPPING_VALUE_PREFIX = "nifi.security.group.mapping.value.";
|
||||
public static final String SECURITY_GROUP_MAPPING_TRANSFORM_PREFIX = "nifi.security.group.mapping.transform.";
|
||||
|
||||
// oidc
|
||||
public static final String SECURITY_USER_OIDC_DISCOVERY_URL = "nifi.security.user.oidc.discovery.url";
|
||||
|
|
|
@ -18,19 +18,33 @@ package org.apache.nifi.authorization.util;
|
|||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.nifi.authorization.util.IdentityMapping.Transform.NONE;
|
||||
|
||||
/**
|
||||
* Holder to pass around the key, pattern, and replacement from an identity mapping in NiFiProperties.
|
||||
*/
|
||||
public class IdentityMapping {
|
||||
|
||||
public enum Transform {
|
||||
NONE,
|
||||
UPPER,
|
||||
LOWER
|
||||
}
|
||||
|
||||
private final String key;
|
||||
private final Pattern pattern;
|
||||
private final String replacementValue;
|
||||
private final Transform transform;
|
||||
|
||||
public IdentityMapping(String key, Pattern pattern, String replacementValue) {
|
||||
this(key, pattern, replacementValue, NONE);
|
||||
}
|
||||
|
||||
public IdentityMapping(String key, Pattern pattern, String replacementValue, Transform transform) {
|
||||
this.key = key;
|
||||
this.pattern = pattern;
|
||||
this.replacementValue = replacementValue;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
|
@ -45,4 +59,7 @@ public class IdentityMapping {
|
|||
return replacementValue;
|
||||
}
|
||||
|
||||
public Transform getTransform() {
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.nifi.authorization.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.util.IdentityMapping.Transform;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -25,9 +26,17 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_GROUP_MAPPING_PATTERN_PREFIX;
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_GROUP_MAPPING_TRANSFORM_PREFIX;
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_GROUP_MAPPING_VALUE_PREFIX;
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX;
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_IDENTITY_MAPPING_TRANSFORM_PREFIX;
|
||||
import static org.apache.nifi.util.NiFiProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX;
|
||||
|
||||
public class IdentityMappingUtil {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(IdentityMappingUtil.class);
|
||||
|
@ -40,33 +49,76 @@ public class IdentityMappingUtil {
|
|||
* @return a list of identity mappings
|
||||
*/
|
||||
public static List<IdentityMapping> getIdentityMappings(final NiFiProperties properties) {
|
||||
return getMappings(
|
||||
properties,
|
||||
SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX,
|
||||
SECURITY_IDENTITY_MAPPING_VALUE_PREFIX,
|
||||
SECURITY_IDENTITY_MAPPING_TRANSFORM_PREFIX,
|
||||
() -> "Identity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Buils the group mappings from NiFiProperties.
|
||||
*
|
||||
* @param properties the NiFiProperties instance
|
||||
* @return a list of group mappings
|
||||
*/
|
||||
public static List<IdentityMapping> getGroupMappings(final NiFiProperties properties) {
|
||||
return getMappings(
|
||||
properties,
|
||||
SECURITY_GROUP_MAPPING_PATTERN_PREFIX,
|
||||
SECURITY_GROUP_MAPPING_VALUE_PREFIX,
|
||||
SECURITY_GROUP_MAPPING_TRANSFORM_PREFIX,
|
||||
() -> "Group");
|
||||
}
|
||||
|
||||
private static List<IdentityMapping> getMappings(final NiFiProperties properties, final String patternPrefix,
|
||||
final String valuePrefix, final String transformPrefix, final Supplier<String> getSubject) {
|
||||
|
||||
final List<IdentityMapping> mappings = new ArrayList<>();
|
||||
|
||||
// go through each property
|
||||
for (String propertyName : properties.getPropertyKeys()) {
|
||||
if (StringUtils.startsWith(propertyName, NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX)) {
|
||||
final String key = StringUtils.substringAfter(propertyName, NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX);
|
||||
if (StringUtils.startsWith(propertyName, patternPrefix)) {
|
||||
final String key = StringUtils.substringAfter(propertyName, patternPrefix);
|
||||
final String identityPattern = properties.getProperty(propertyName);
|
||||
|
||||
if (StringUtils.isBlank(identityPattern)) {
|
||||
LOGGER.warn("Identity Mapping property {} was found, but was empty", new Object[]{propertyName});
|
||||
LOGGER.warn("{} Mapping property {} was found, but was empty", new Object[] {getSubject.get(), propertyName});
|
||||
continue;
|
||||
}
|
||||
|
||||
final String identityValueProperty = NiFiProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX + key;
|
||||
final String identityValueProperty = valuePrefix + key;
|
||||
final String identityValue = properties.getProperty(identityValueProperty);
|
||||
|
||||
if (StringUtils.isBlank(identityValue)) {
|
||||
LOGGER.warn("Identity Mapping property {} was found, but corresponding value {} was not found",
|
||||
new Object[]{propertyName, identityValueProperty});
|
||||
LOGGER.warn("{} Mapping property {} was found, but corresponding value {} was not found",
|
||||
new Object[] {getSubject.get(), propertyName, identityValueProperty});
|
||||
continue;
|
||||
}
|
||||
|
||||
final IdentityMapping identityMapping = new IdentityMapping(key, Pattern.compile(identityPattern), identityValue);
|
||||
final String identityTransformProperty = transformPrefix + key;
|
||||
String rawIdentityTransform = properties.getProperty(identityTransformProperty);
|
||||
|
||||
if (StringUtils.isBlank(rawIdentityTransform)) {
|
||||
LOGGER.debug("{} Mapping property {} was found, but no transform was present. Using NONE.", new Object[] {getSubject.get(), propertyName});
|
||||
rawIdentityTransform = Transform.NONE.name();
|
||||
}
|
||||
|
||||
final Transform identityTransform;
|
||||
try {
|
||||
identityTransform = Transform.valueOf(rawIdentityTransform);
|
||||
} catch (final IllegalArgumentException iae) {
|
||||
LOGGER.warn("{} Mapping property {} was found, but corresponding transform {} was not valid. Allowed values {}",
|
||||
new Object[] {getSubject.get(), propertyName, rawIdentityTransform, StringUtils.join(Transform.values(), ", ")});
|
||||
continue;
|
||||
}
|
||||
|
||||
final IdentityMapping identityMapping = new IdentityMapping(key, Pattern.compile(identityPattern), identityValue, identityTransform);
|
||||
mappings.add(identityMapping);
|
||||
|
||||
LOGGER.debug("Found Identity Mapping with key = {}, pattern = {}, value = {}",
|
||||
new Object[] {key, identityPattern, identityValue});
|
||||
LOGGER.debug("Found {} Mapping with key = {}, pattern = {}, value = {}, transform = {}",
|
||||
new Object[] {getSubject.get(), key, identityPattern, identityValue, rawIdentityTransform});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +147,15 @@ public class IdentityMappingUtil {
|
|||
if (m.matches()) {
|
||||
final String pattern = mapping.getPattern().pattern();
|
||||
final String replacementValue = escapeLiteralBackReferences(mapping.getReplacementValue(), m.groupCount());
|
||||
return identity.replaceAll(pattern, replacementValue);
|
||||
final String replacement = identity.replaceAll(pattern, replacementValue);
|
||||
|
||||
if (Transform.UPPER.equals(mapping.getTransform())) {
|
||||
return replacement.toUpperCase();
|
||||
} else if (Transform.LOWER.equals(mapping.getTransform())) {
|
||||
return replacement.toLowerCase();
|
||||
} else {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3406,13 +3406,27 @@ The following examples demonstrate normalizing DNs from certificates and princip
|
|||
----
|
||||
nifi.security.identity.mapping.pattern.dn=^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$
|
||||
nifi.security.identity.mapping.value.dn=$1@$2
|
||||
nifi.security.identity.mapping.transform.dn=NONE
|
||||
nifi.security.identity.mapping.pattern.kerb=^(.*?)/instance@(.*?)$
|
||||
nifi.security.identity.mapping.value.kerb=$1@$2
|
||||
nifi.security.identity.mapping.transform.kerb=NONE
|
||||
----
|
||||
|
||||
The last segment of each property is an identifier used to associate the pattern with the replacement value. When a user makes a request to NiFi, their identity is checked to see if it matches each of those patterns in lexicographical order. For the first one that matches, the replacement specified in the `nifi.security.identity.mapping.value.xxxx` property is used. So a login with `CN=localhost, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US` matches the DN mapping pattern above and the DN mapping value `$1@$2` is applied. The user is normalized to `localhost@Apache NiFi`.
|
||||
|
||||
NOTE: These mappings are also applied to the "Initial Admin Identity" and "Cluster Node Identity" properties in the authorizers.xml file (See <<authorizers-setup>>).
|
||||
In addition to mapping a transform may be applied. The supported versions are NONE (no transform applied), LOWER (identity lowercased), and UPPER (identity uppercased). If not specified, the default value is NONE.
|
||||
|
||||
NOTE: These mappings are also applied to the "Initial Admin Identity", "Cluster Node Identity", and any legacy users in the authorizers.xml file as well as users imported from LDAP (See <<authorizers-setup>>).
|
||||
|
||||
Group names can also be mapped. The following example will accept the existing group name but will lowercase it. This may be helpful when used in conjunction with an external authorizer.
|
||||
|
||||
----
|
||||
nifi.security.group.mapping.pattern.anygroup=^(.*)$
|
||||
nifi.security.group.mapping.value.anygroup=$1
|
||||
nifi.security.group.mapping.transform.anygroup=LOWER
|
||||
----
|
||||
|
||||
NOTE: These mappings are applied to any legacy groups referenced in the authorizers.xml as well as groups imported from LDAP.
|
||||
|
||||
=== Cluster Common Properties
|
||||
|
||||
|
|
|
@ -16,39 +16,6 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.annotation.AuthorizerContext;
|
||||
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
|
||||
|
@ -76,6 +43,40 @@ import org.w3c.dom.Node;
|
|||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileAccessPolicyProvider.class);
|
||||
|
@ -130,6 +131,7 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
|
|||
private Set<String> nodeIdentities;
|
||||
private List<PortDTO> ports = new ArrayList<>();
|
||||
private List<IdentityMapping> identityMappings;
|
||||
private List<IdentityMapping> groupMappings;
|
||||
|
||||
private UserGroupProvider userGroupProvider;
|
||||
private UserGroupProviderLookup userGroupProviderLookup;
|
||||
|
@ -200,6 +202,7 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
|
|||
|
||||
// extract the identity mappings from nifi.properties if any are provided
|
||||
identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
|
||||
groupMappings = Collections.unmodifiableList(IdentityMappingUtil.getGroupMappings(properties));
|
||||
|
||||
// get the value of the initial admin identity
|
||||
final PropertyValue initialAdminIdentityProp = configurationContext.getProperty(PROP_INITIAL_ADMIN_IDENTITY);
|
||||
|
@ -728,10 +731,12 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
|
|||
|
||||
if (portDTO.getGroupAccessControl() != null) {
|
||||
for (String groupAccessControl : portDTO.getGroupAccessControl()) {
|
||||
final String legacyGroupName = IdentityMappingUtil.mapIdentity(groupAccessControl, groupMappings);
|
||||
|
||||
// find a group where the name is the groupAccessControl
|
||||
Group foundGroup = null;
|
||||
for (Group group : userGroupProvider.getGroups()) {
|
||||
if (group.getName().equals(groupAccessControl)) {
|
||||
if (group.getName().equals(legacyGroupName)) {
|
||||
foundGroup = group;
|
||||
break;
|
||||
}
|
||||
|
@ -740,7 +745,7 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
|
|||
// couldn't find the group matching the access control so log a warning and skip
|
||||
if (foundGroup == null) {
|
||||
logger.warn("Found port with group access control for {} but no group exists with this name, skipping...",
|
||||
new Object[] {groupAccessControl});
|
||||
new Object[] {legacyGroupName});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -751,7 +756,7 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
|
|||
resource.getIdentifier(),
|
||||
WRITE_CODE);
|
||||
|
||||
addGroupToPolicy(IdentifierUtil.getIdentifier(groupAccessControl), policy);
|
||||
addGroupToPolicy(IdentifierUtil.getIdentifier(legacyGroupName), policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,39 +16,6 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.annotation.AuthorizerContext;
|
||||
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
|
||||
|
@ -72,6 +39,40 @@ import org.w3c.dom.Node;
|
|||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileUserGroupProvider.class);
|
||||
|
@ -118,6 +119,7 @@ public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
|
|||
private String legacyAuthorizedUsersFile;
|
||||
private Set<String> initialUserIdentities;
|
||||
private List<IdentityMapping> identityMappings;
|
||||
private List<IdentityMapping> groupMappings;
|
||||
|
||||
private final AtomicReference<UserGroupHolder> userGroupHolder = new AtomicReference<>();
|
||||
|
||||
|
@ -172,8 +174,9 @@ public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// extract the identity mappings from nifi.properties if any are provided
|
||||
// extract the identity and group mappings from nifi.properties if any are provided
|
||||
identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
|
||||
groupMappings = Collections.unmodifiableList(IdentityMappingUtil.getGroupMappings(properties));
|
||||
|
||||
// get the value of the legacy authorized users file
|
||||
final PropertyValue legacyAuthorizedUsersProp = configurationContext.getProperty(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
|
@ -697,8 +700,9 @@ public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
|
|||
org.apache.nifi.authorization.file.tenants.generated.User user = getOrCreateUser(tenants, legacyUserDn);
|
||||
|
||||
// if there was a group name find or create the group and add the user to it
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group group = getOrCreateGroup(tenants, legacyUser.getGroup());
|
||||
if (group != null) {
|
||||
if (StringUtils.isNotBlank(legacyUser.getGroup())) {
|
||||
final String legacyGroupName = IdentityMappingUtil.mapIdentity(legacyUser.getGroup(), groupMappings);
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group group = getOrCreateGroup(tenants, legacyGroupName);
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group.User groupUser = new org.apache.nifi.authorization.file.tenants.generated.Group.User();
|
||||
groupUser.setIdentifier(user.getIdentifier());
|
||||
group.getUser().add(groupUser);
|
||||
|
|
|
@ -435,6 +435,10 @@ public class FileAuthorizerTest {
|
|||
props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$");
|
||||
props.setProperty("nifi.security.identity.mapping.value.dn1", "$1");
|
||||
|
||||
props.setProperty("nifi.security.group.mapping.pattern.anygroup", "^(.*)$");
|
||||
props.setProperty("nifi.security.group.mapping.value.anygroup", "$1");
|
||||
props.setProperty("nifi.security.group.mapping.transform.anygroup", "UPPER");
|
||||
|
||||
properties = getNiFiProperties(props);
|
||||
when(properties.getRestoreDirectory()).thenReturn(restoreAuthorizations.getParentFile());
|
||||
when(properties.getFlowConfigurationFile()).thenReturn(flowWithDns);
|
||||
|
@ -469,7 +473,7 @@ public class FileAuthorizerTest {
|
|||
final Set<Group> groups = authorizer.getGroups();
|
||||
assertEquals(1, groups.size());
|
||||
final Group group1 = groups.iterator().next();
|
||||
assertEquals("group1", group1.getName());
|
||||
assertEquals("GROUP1", group1.getName());
|
||||
|
||||
final Resource inputPortResource = ResourceFactory.getDataTransferResource(
|
||||
ResourceFactory.getComponentResource(ResourceType.InputPort, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"));
|
||||
|
|
|
@ -227,6 +227,52 @@ public class FileUserGroupProviderTest {
|
|||
assertEquals("group1", group1.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenLegacyUsersFileProvidedWithIdentityMappingsAndTransforms() throws Exception {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$");
|
||||
props.setProperty("nifi.security.identity.mapping.value.dn1", "$1");
|
||||
props.setProperty("nifi.security.identity.mapping.transform.dn1", "UPPER");
|
||||
|
||||
props.setProperty("nifi.security.group.mapping.pattern.anygroup", "^(.*)$");
|
||||
props.setProperty("nifi.security.group.mapping.value.anygroup", "$1");
|
||||
props.setProperty("nifi.security.group.mapping.transform.anygroup", "UPPER");
|
||||
|
||||
properties = getNiFiProperties(props);
|
||||
when(properties.getRestoreDirectory()).thenReturn(restoreTenants.getParentFile());
|
||||
userGroupProvider.setNiFiProperties(properties);
|
||||
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
.thenReturn(new StandardPropertyValue("src/test/resources/authorized-users-with-dns.xml", null));
|
||||
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final User user1 = userGroupProvider.getUserByIdentity("USER1");
|
||||
assertNotNull(user1);
|
||||
|
||||
final User user2 = userGroupProvider.getUserByIdentity("USER2");
|
||||
assertNotNull(user2);
|
||||
|
||||
final User user3 = userGroupProvider.getUserByIdentity("USER3");
|
||||
assertNotNull(user3);
|
||||
|
||||
final User user4 = userGroupProvider.getUserByIdentity("USER4");
|
||||
assertNotNull(user4);
|
||||
|
||||
final User user5 = userGroupProvider.getUserByIdentity("USER5");
|
||||
assertNotNull(user5);
|
||||
|
||||
final User user6 = userGroupProvider.getUserByIdentity("USER6");
|
||||
assertNotNull(user6);
|
||||
|
||||
// verify one group got created
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(1, groups.size());
|
||||
final Group group1 = groups.iterator().next();
|
||||
assertEquals("GROUP1", group1.getName());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenBadLegacyUsersFileProvided() throws Exception {
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
|
|
|
@ -186,8 +186,18 @@ nifi.security.user.knox.audiences=${nifi.security.user.knox.audiences}
|
|||
#
|
||||
# nifi.security.identity.mapping.pattern.dn=^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$
|
||||
# nifi.security.identity.mapping.value.dn=$1@$2
|
||||
# nifi.security.identity.mapping.transform.dn=NONE
|
||||
# nifi.security.identity.mapping.pattern.kerb=^(.*?)/instance@(.*?)$
|
||||
# nifi.security.identity.mapping.value.kerb=$1@$2
|
||||
# nifi.security.identity.mapping.transform.kerb=UPPER
|
||||
|
||||
# Group Mapping Properties #
|
||||
# These properties allow normalizing group names coming from external sources like LDAP. The following example
|
||||
# lowercases any group name.
|
||||
#
|
||||
# nifi.security.group.mapping.pattern.anygroup=^(.*)$
|
||||
# nifi.security.group.mapping.value.anygroup=$1
|
||||
# nifi.security.group.mapping.transform.anygroup=LOWER
|
||||
|
||||
# cluster common properties (all nodes must have same values) #
|
||||
nifi.cluster.protocol.heartbeat.interval=${nifi.cluster.protocol.heartbeat.interval}
|
||||
|
|
|
@ -47,6 +47,8 @@ public class TestStandardRootGroupPort {
|
|||
final AuthorizationRequest request = invocation.getArgumentAt(0, AuthorizationRequest.class);
|
||||
if ("node1@nifi.test".equals(request.getIdentity())) {
|
||||
return AuthorizationResult.approved();
|
||||
} else if ("NODE1@NIFI.TEST".equals(request.getIdentity())) {
|
||||
return AuthorizationResult.approved();
|
||||
}
|
||||
return AuthorizationResult.denied();
|
||||
}).when(authorizer).authorize(any(AuthorizationRequest.class));
|
||||
|
@ -97,4 +99,31 @@ public class TestStandardRootGroupPort {
|
|||
Assert.assertTrue(authResult.isAuthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckUserAuthorizationByMappedDnWithTransformation() {
|
||||
|
||||
final NiFiProperties nifiProperties = mock(NiFiProperties.class);
|
||||
final String mapKey = ".dn";
|
||||
Set<String> propertyKeys = new LinkedHashSet<>();
|
||||
propertyKeys.add(NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX + mapKey);
|
||||
propertyKeys.add(NiFiProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX + mapKey);
|
||||
propertyKeys.add(NiFiProperties.SECURITY_IDENTITY_MAPPING_TRANSFORM_PREFIX + mapKey);
|
||||
doReturn(propertyKeys).when(nifiProperties).getPropertyKeys();
|
||||
|
||||
final String mapPattern = "^CN=(.*?), OU=(.*?)$";
|
||||
final String mapValue = "$1@$2";
|
||||
final String mapTransform = "UPPER";
|
||||
doReturn(mapPattern).when(nifiProperties).getProperty(eq(NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX + mapKey));
|
||||
doReturn(mapValue).when(nifiProperties).getProperty(eq(NiFiProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX + mapKey));
|
||||
doReturn(mapTransform).when(nifiProperties).getProperty(eq(NiFiProperties.SECURITY_IDENTITY_MAPPING_TRANSFORM_PREFIX + mapKey));
|
||||
|
||||
final RootGroupPort port = createRootGroupPort(nifiProperties);
|
||||
|
||||
PortAuthorizationResult authResult = port.checkUserAuthorization("CN=node2, OU=nifi.test");
|
||||
Assert.assertFalse(authResult.isAuthorized());
|
||||
|
||||
authResult = port.checkUserAuthorization("CN=node1, OU=nifi.test");
|
||||
Assert.assertTrue(authResult.isAuthorized());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
private static final long MINIMUM_SYNC_INTERVAL_MILLISECONDS = 10_000;
|
||||
|
||||
private List<IdentityMapping> identityMappings;
|
||||
private List<IdentityMapping> groupMappings;
|
||||
private NiFiProperties properties;
|
||||
|
||||
private ScheduledExecutorService ldapSync;
|
||||
|
@ -350,6 +351,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
|
||||
// extract the identity mappings from nifi.properties if any are provided
|
||||
identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
|
||||
groupMappings = Collections.unmodifiableList(IdentityMappingUtil.getGroupMappings(properties));
|
||||
|
||||
// set the base environment is necessary
|
||||
if (!baseEnvironment.isEmpty()) {
|
||||
|
@ -708,7 +710,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
return IdentityMappingUtil.mapIdentity(name, groupMappings);
|
||||
}
|
||||
|
||||
private String getReferencedGroupValue(final DirContextOperations ctx) {
|
||||
|
|
|
@ -507,6 +507,49 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
|
|||
assertNotNull(ldapUserGroupProvider.getUserByIdentity("User 1,ou=users"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserIdentityMappingWithTransforms() throws Exception {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^cn=(.*?),ou=(.*?),o=(.*?)$");
|
||||
props.setProperty("nifi.security.identity.mapping.value.dn1", "$1");
|
||||
props.setProperty("nifi.security.identity.mapping.transform.dn1", "UPPER");
|
||||
|
||||
final NiFiProperties properties = getNiFiProperties(props);
|
||||
ldapUserGroupProvider.setNiFiProperties(properties);
|
||||
|
||||
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null);
|
||||
when(configurationContext.getProperty(PROP_USER_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(uid=user1)", null));
|
||||
ldapUserGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
assertEquals(1, ldapUserGroupProvider.getUsers().size());
|
||||
assertNotNull(ldapUserGroupProvider.getUserByIdentity("USER 1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserIdentityAndGroupMappingWithTransforms() throws Exception {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^cn=(.*?),ou=(.*?),o=(.*?)$");
|
||||
props.setProperty("nifi.security.identity.mapping.value.dn1", "$1");
|
||||
props.setProperty("nifi.security.identity.mapping.transform.dn1", "UPPER");
|
||||
props.setProperty("nifi.security.group.mapping.pattern.dn1", "^cn=(.*?),ou=(.*?),o=(.*?)$");
|
||||
props.setProperty("nifi.security.group.mapping.value.dn1", "$1");
|
||||
props.setProperty("nifi.security.group.mapping.transform.dn1", "UPPER");
|
||||
|
||||
final NiFiProperties properties = getNiFiProperties(props);
|
||||
ldapUserGroupProvider.setNiFiProperties(properties);
|
||||
|
||||
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, GROUP_SEARCH_BASE);
|
||||
when(configurationContext.getProperty(PROP_USER_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(uid=user1)", null));
|
||||
when(configurationContext.getProperty(PROP_GROUP_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(cn=admins)", null));
|
||||
ldapUserGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
assertEquals(1, ldapUserGroupProvider.getUsers().size());
|
||||
assertNotNull(ldapUserGroupProvider.getUserByIdentity("USER 1"));
|
||||
|
||||
assertEquals(1, ldapUserGroupProvider.getGroups().size());
|
||||
assertEquals("ADMINS", ldapUserGroupProvider.getGroups().iterator().next().getName());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testReferencedGroupAttributeWithoutGroupSearchBase() throws Exception {
|
||||
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", null);
|
||||
|
|
Loading…
Reference in New Issue