mirror of https://github.com/apache/nifi.git
NIFI-2003 Creating abstract authentication provider and incorporating into existing providers
NIFI-2201 Add support for seeding cluster nodes in authorizations.xml - Passing client address along in user context on authorization requests - This closes #628
This commit is contained in:
parent
4b9df7d1e2
commit
ba763b95e8
|
@ -105,31 +105,33 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
final UsersAndAccessPolicies usersAndAccessPolicies = getUsersAndAccessPolicies();
|
||||
final String resourceIdentifier = request.getResource().getIdentifier();
|
||||
|
||||
final Set<AccessPolicy> policies = usersAndAccessPolicies.getAccessPolicies(resourceIdentifier);
|
||||
if (policies == null || policies.isEmpty()) {
|
||||
final AccessPolicy policy = usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, request.getAction());
|
||||
if (policy == null) {
|
||||
return AuthorizationResult.resourceNotFound();
|
||||
}
|
||||
|
||||
final User user = usersAndAccessPolicies.getUser(request.getIdentity());
|
||||
|
||||
if (user == null) {
|
||||
return AuthorizationResult.denied("Unknown user with identity " + request.getIdentity());
|
||||
}
|
||||
|
||||
final Set<Group> userGroups = usersAndAccessPolicies.getGroups(user.getIdentity());
|
||||
|
||||
for (AccessPolicy policy : policies) {
|
||||
final boolean containsUser = policy.getUsers().contains(user.getIdentifier());
|
||||
if (policy.getAction() == request.getAction() && (containsUser || containsGroup(userGroups, policy)) ) {
|
||||
return AuthorizationResult.approved();
|
||||
}
|
||||
if (policy.getUsers().contains(user.getIdentifier()) || containsGroup(userGroups, policy)) {
|
||||
return AuthorizationResult.approved();
|
||||
}
|
||||
|
||||
|
||||
return AuthorizationResult.denied();
|
||||
}
|
||||
|
||||
|
||||
private boolean containsGroup(Set<Group> userGroups, final AccessPolicy policy) {
|
||||
/**
|
||||
* Determines if the policy contains one of the user's groups.
|
||||
*
|
||||
* @param userGroups the set of the user's groups
|
||||
* @param policy the policy
|
||||
* @return true if one of the Groups in userGroups is contained in the policy
|
||||
*/
|
||||
private boolean containsGroup(final Set<Group> userGroups, final AccessPolicy policy) {
|
||||
if (userGroups.isEmpty() || policy.getGroups().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.authorization;
|
||||
|
||||
/**
|
||||
* Constants for keys that can be passed in the AuthorizationRequest user context Map.
|
||||
*/
|
||||
public enum UserContextKeys {
|
||||
|
||||
CLIENT_ADDRESS;
|
||||
|
||||
}
|
|
@ -25,12 +25,13 @@ import java.util.Set;
|
|||
public interface UsersAndAccessPolicies {
|
||||
|
||||
/**
|
||||
* Retrieves the set of access policies for a given resource.
|
||||
* Retrieves the set of access policies for a given resource and action.
|
||||
*
|
||||
* @param resourceIdentifier the resource identifier to retrieve policies for
|
||||
* @return the set of access policies for the given resource
|
||||
* @param action the action to retrieve policies for
|
||||
* @return the access policy for the given resource and action
|
||||
*/
|
||||
public Set<AccessPolicy> getAccessPolicies(final String resourceIdentifier);
|
||||
public AccessPolicy getAccessPolicy(final String resourceIdentifier, final RequestAction action);
|
||||
|
||||
/**
|
||||
* Retrieves a user by an identity string.
|
||||
|
|
|
@ -23,9 +23,11 @@ import org.apache.nifi.authorization.AuthorizationResult.Result;
|
|||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
public interface Authorizable {
|
||||
|
||||
|
@ -67,7 +69,13 @@ public interface Authorizable {
|
|||
* @return is authorized
|
||||
*/
|
||||
default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
|
||||
// TODO - include user details context
|
||||
final Map<String,String> userContext;
|
||||
if (user.getClientAddress() != null && !user.getClientAddress().trim().isEmpty()) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
// build the request
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
|
@ -77,6 +85,7 @@ public interface Authorizable {
|
|||
.action(action)
|
||||
.resource(getResource())
|
||||
.resourceContext(resourceContext)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
// perform the authorization
|
||||
|
@ -119,7 +128,13 @@ public interface Authorizable {
|
|||
* @param resourceContext resource context
|
||||
*/
|
||||
default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
|
||||
// TODO - include user details context
|
||||
final Map<String,String> userContext;
|
||||
if (user.getClientAddress() != null && !user.getClientAddress().trim().isEmpty()) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(user.getIdentity())
|
||||
|
@ -128,6 +143,7 @@ public interface Authorizable {
|
|||
.action(action)
|
||||
.resource(getResource())
|
||||
.resourceContext(resourceContext)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -36,4 +36,10 @@ public interface NiFiUser {
|
|||
* @return <code>true</code> if the user is the unauthenticated Anonymous user
|
||||
*/
|
||||
boolean isAnonymous();
|
||||
|
||||
/**
|
||||
* @return the address of the client that made the request which created this user
|
||||
*/
|
||||
String getClientAddress();
|
||||
|
||||
}
|
||||
|
|
|
@ -54,15 +54,14 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
|
||||
final Set<AccessPolicy> policiesForResource = new HashSet<>();
|
||||
policiesForResource.add(new AccessPolicy.Builder()
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addUser(userIdentifier)
|
||||
.action(RequestAction.READ)
|
||||
.build());
|
||||
.build();
|
||||
|
||||
when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource);
|
||||
when(usersAndAccessPolicies.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
|
@ -92,15 +91,14 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
final String userIdentity = "userIdentity1";
|
||||
final String groupIdentifier = "groupIdentifier1";
|
||||
|
||||
final Set<AccessPolicy> policiesForResource = new HashSet<>();
|
||||
policiesForResource.add(new AccessPolicy.Builder()
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addGroup(groupIdentifier)
|
||||
.action(RequestAction.READ)
|
||||
.build());
|
||||
.build();
|
||||
|
||||
when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource);
|
||||
when(usersAndAccessPolicies.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
|
@ -137,15 +135,14 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
|
||||
final Set<AccessPolicy> policiesForResource = new HashSet<>();
|
||||
policiesForResource.add(new AccessPolicy.Builder()
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addUser("NOT_USER_1")
|
||||
.action(RequestAction.READ)
|
||||
.build());
|
||||
.build();
|
||||
|
||||
when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource);
|
||||
when(usersAndAccessPolicies.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
|
@ -171,7 +168,7 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
UsersAndAccessPolicies usersAndAccessPolicies = Mockito.mock(UsersAndAccessPolicies.class);
|
||||
when(authorizer.getUsersAndAccessPolicies()).thenReturn(usersAndAccessPolicies);
|
||||
|
||||
when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(new HashSet<>());
|
||||
when(usersAndAccessPolicies.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(null);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity("userIdentity")
|
||||
|
|
|
@ -135,6 +135,8 @@ public class NiFiProperties extends Properties {
|
|||
public static final String SECURITY_CLUSTER_AUTHORITY_PROVIDER_THREADS = "nifi.security.cluster.authority.provider.threads";
|
||||
public static final String SECURITY_OCSP_RESPONDER_URL = "nifi.security.ocsp.responder.url";
|
||||
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.";
|
||||
|
||||
// web properties
|
||||
public static final String WEB_WAR_DIR = "nifi.web.war.directory";
|
||||
|
|
|
@ -337,11 +337,23 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies(String resourceIdentifier) {
|
||||
public AccessPolicy getAccessPolicy(final String resourceIdentifier, final RequestAction action) {
|
||||
if (resourceIdentifier == null) {
|
||||
throw new IllegalArgumentException("Resource Identifier cannot be null");
|
||||
}
|
||||
return policiesByResource.get(resourceIdentifier);
|
||||
|
||||
final Set<AccessPolicy> resourcePolicies = policiesByResource.get(resourceIdentifier);
|
||||
if (resourcePolicies == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (AccessPolicy accessPolicy : resourcePolicies) {
|
||||
if (accessPolicy.getAction() == action) {
|
||||
return accessPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -66,6 +66,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
|
@ -103,6 +105,7 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
static final String PROP_AUTHORIZATIONS_FILE = "Authorizations File";
|
||||
static final String PROP_INITIAL_ADMIN_IDENTITY = "Initial Admin Identity";
|
||||
static final String PROP_LEGACY_AUTHORIZED_USERS_FILE = "Legacy Authorized Users File";
|
||||
static final Pattern NODE_IDENTITY_PATTERN = Pattern.compile("Node Identity \\S+");
|
||||
|
||||
private Schema flowSchema;
|
||||
private Schema usersSchema;
|
||||
|
@ -114,6 +117,7 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
private String rootGroupId;
|
||||
private String initialAdminIdentity;
|
||||
private String legacyAuthorizedUsersFile;
|
||||
private Set<String> nodeIdentities;
|
||||
|
||||
private final AtomicReference<AuthorizationsHolder> authorizationsHolder = new AtomicReference<>();
|
||||
|
||||
|
@ -169,12 +173,23 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
}
|
||||
}
|
||||
|
||||
// get the value of the initial admin identity
|
||||
final PropertyValue initialAdminIdentityProp = configurationContext.getProperty(PROP_INITIAL_ADMIN_IDENTITY);
|
||||
initialAdminIdentity = initialAdminIdentityProp == null ? null : initialAdminIdentityProp.getValue();
|
||||
|
||||
// get the value of the legacy authorized users file
|
||||
final PropertyValue legacyAuthorizedUsersProp = configurationContext.getProperty(PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
legacyAuthorizedUsersFile = legacyAuthorizedUsersProp == null ? null : legacyAuthorizedUsersProp.getValue();
|
||||
|
||||
// extract any node identities
|
||||
nodeIdentities = new HashSet<>();
|
||||
for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
|
||||
Matcher matcher = NODE_IDENTITY_PATTERN.matcher(entry.getKey());
|
||||
if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
|
||||
nodeIdentities.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// load the authorizations
|
||||
load();
|
||||
|
||||
|
@ -235,6 +250,8 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
convertLegacyAuthorizedUsers(authorizations);
|
||||
}
|
||||
|
||||
populateNodes(authorizations);
|
||||
|
||||
// save any changes that were made and repopulate the holder
|
||||
saveAndRefreshHolder(authorizations);
|
||||
} else {
|
||||
|
@ -337,6 +354,38 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
addAccessPolicy(authorizations, ResourceType.Policy.getValue(), adminUser.getIdentifier(), WRITE_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user for each node and gives the nodes write permission to /proxy.
|
||||
*
|
||||
* @param authorizations the overall authorizations
|
||||
*/
|
||||
private void populateNodes(Authorizations authorizations) {
|
||||
for (String nodeIdentity : nodeIdentities) {
|
||||
// see if we have an existing user for the given node identity
|
||||
org.apache.nifi.authorization.file.generated.User jaxbNodeUser = null;
|
||||
for (org.apache.nifi.authorization.file.generated.User user : authorizations.getUsers().getUser()) {
|
||||
if (user.getIdentity().equals(nodeIdentity)) {
|
||||
jaxbNodeUser = user;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find an existing user then create a new one
|
||||
if (jaxbNodeUser == null) {
|
||||
// generate an identifier and add a User with the given identifier and identity
|
||||
final UUID nodeIdentifier = UUID.nameUUIDFromBytes(nodeIdentity.getBytes(StandardCharsets.UTF_8));
|
||||
final User nodeUser = new User.Builder().identifier(nodeIdentifier.toString()).identity(nodeIdentity).build();
|
||||
|
||||
jaxbNodeUser = createJAXBUser(nodeUser);
|
||||
authorizations.getUsers().getUser().add(jaxbNodeUser);
|
||||
}
|
||||
|
||||
// grant access to the proxy resource
|
||||
addAccessPolicy(authorizations, ResourceType.Proxy.getValue(), jaxbNodeUser.getIdentifier(), READ_CODE);
|
||||
addAccessPolicy(authorizations, ResourceType.Proxy.getValue(), jaxbNodeUser.getIdentifier(), WRITE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshalls an existing authorized-users.xml and converts the object model to the new model.
|
||||
*
|
||||
|
@ -509,25 +558,42 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
|
|||
* @param action the action for the policy
|
||||
*/
|
||||
private void addAccessPolicy(final Authorizations authorizations, final String resource, final String identity, final String action) {
|
||||
final String uuidSeed = resource + identity;
|
||||
final UUID policyIdentifier = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder()
|
||||
.identifier(policyIdentifier.toString())
|
||||
.resource(resource)
|
||||
.addUser(identity);
|
||||
|
||||
if (action.equals(READ_CODE)) {
|
||||
builder.action(RequestAction.READ);
|
||||
} else if (action.equals(WRITE_CODE)) {
|
||||
builder.action(RequestAction.WRITE);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown Policy Action: " + action);
|
||||
// first try to find an existing policy for the given resource and action
|
||||
Policy foundPolicy = null;
|
||||
for (Policy policy : authorizations.getPolicies().getPolicy()) {
|
||||
if (policy.getResource().equals(resource) && policy.getAction().equals(action)) {
|
||||
foundPolicy = policy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final AccessPolicy accessPolicy = builder.build();
|
||||
final Policy jaxbPolicy = createJAXBPolicy(accessPolicy);
|
||||
authorizations.getPolicies().getPolicy().add(jaxbPolicy);
|
||||
if (foundPolicy == null) {
|
||||
// if we didn't find an existing policy create a new one
|
||||
final String uuidSeed = resource + identity + action;
|
||||
final UUID policyIdentifier = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder()
|
||||
.identifier(policyIdentifier.toString())
|
||||
.resource(resource)
|
||||
.addUser(identity);
|
||||
|
||||
if (action.equals(READ_CODE)) {
|
||||
builder.action(RequestAction.READ);
|
||||
} else if (action.equals(WRITE_CODE)) {
|
||||
builder.action(RequestAction.WRITE);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown Policy Action: " + action);
|
||||
}
|
||||
|
||||
final AccessPolicy accessPolicy = builder.build();
|
||||
final Policy jaxbPolicy = createJAXBPolicy(accessPolicy);
|
||||
authorizations.getPolicies().getPolicy().add(jaxbPolicy);
|
||||
} else {
|
||||
// otherwise add the user to the existing policy
|
||||
Policy.User policyUser = new Policy.User();
|
||||
policyUser.setIdentifier(identity);
|
||||
foundPolicy.getUser().add(policyUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -157,10 +157,12 @@ public class FileAuthorizerTest {
|
|||
assertEquals(1, users.size());
|
||||
|
||||
UsersAndAccessPolicies usersAndAccessPolicies = authorizer.getUsersAndAccessPolicies();
|
||||
assertEquals(1, usersAndAccessPolicies.getAccessPolicies(ResourceType.Flow.getValue()).size());
|
||||
assertEquals(2, usersAndAccessPolicies.getAccessPolicies(ResourceType.Controller.getValue()).size());
|
||||
assertEquals(1, usersAndAccessPolicies.getAccessPolicies(ResourceType.System.getValue()).size());
|
||||
assertEquals(2, usersAndAccessPolicies.getAccessPolicies(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID).size());
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.Flow.getValue(), RequestAction.READ));
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.Controller.getValue(), RequestAction.READ));
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.Controller.getValue(), RequestAction.WRITE));
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.System.getValue(), RequestAction.READ));
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID, RequestAction.READ));
|
||||
assertNotNull(usersAndAccessPolicies.getAccessPolicy(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID, RequestAction.WRITE));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -340,7 +342,7 @@ public class FileAuthorizerTest {
|
|||
assertEquals(adminIdentity, adminUser.getIdentity());
|
||||
|
||||
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
|
||||
assertEquals(4, policies.size());
|
||||
assertEquals(7, policies.size());
|
||||
|
||||
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;
|
||||
|
||||
|
@ -377,7 +379,7 @@ public class FileAuthorizerTest {
|
|||
assertEquals(adminIdentity, adminUser.getIdentity());
|
||||
|
||||
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
|
||||
assertEquals(3, policies.size());
|
||||
assertEquals(5, policies.size());
|
||||
|
||||
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;
|
||||
|
||||
|
@ -414,7 +416,7 @@ public class FileAuthorizerTest {
|
|||
assertEquals(adminIdentity, adminUser.getIdentity());
|
||||
|
||||
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
|
||||
assertEquals(3, policies.size());
|
||||
assertEquals(5, policies.size());
|
||||
|
||||
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;
|
||||
|
||||
|
@ -429,6 +431,46 @@ public class FileAuthorizerTest {
|
|||
assertFalse(foundRootGroupPolicy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenNodeIdentitiesProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
final String nodeIdentity1 = "node1";
|
||||
final String nodeIdentity2 = "node2";
|
||||
|
||||
final Map<String,String> props = new HashMap<>();
|
||||
props.put("Node Identity 1", nodeIdentity1);
|
||||
props.put("Node Identity 2", nodeIdentity2);
|
||||
|
||||
when(configurationContext.getProperties()).thenReturn(props);
|
||||
|
||||
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
authorizer.onConfigured(configurationContext);
|
||||
|
||||
User adminUser = authorizer.getUserByIdentity(adminIdentity);
|
||||
assertNotNull(adminUser);
|
||||
|
||||
User nodeUser1 = authorizer.getUserByIdentity(nodeIdentity1);
|
||||
assertNotNull(nodeUser1);
|
||||
|
||||
User nodeUser2 = authorizer.getUserByIdentity(nodeIdentity2);
|
||||
assertNotNull(nodeUser2);
|
||||
|
||||
AccessPolicy proxyReadPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(ResourceType.Proxy.getValue(), RequestAction.READ);
|
||||
AccessPolicy proxyWritePolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(ResourceType.Proxy.getValue(), RequestAction.WRITE);
|
||||
|
||||
assertNotNull(proxyReadPolicy);
|
||||
assertTrue(proxyReadPolicy.getUsers().contains(nodeUser1.getIdentifier()));
|
||||
assertTrue(proxyReadPolicy.getUsers().contains(nodeUser2.getIdentifier()));
|
||||
|
||||
assertNotNull(proxyWritePolicy);
|
||||
assertTrue(proxyWritePolicy.getUsers().contains(nodeUser1.getIdentifier()));
|
||||
assertTrue(proxyWritePolicy.getUsers().contains(nodeUser2.getIdentifier()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenAuthorizationsFileDoesNotExist() {
|
||||
authorizer.onConfigured(configurationContext);
|
||||
|
|
|
@ -27,14 +27,24 @@ public class StandardNiFiUser implements NiFiUser {
|
|||
|
||||
private final String identity;
|
||||
private final NiFiUser chain;
|
||||
private final String clientAddress;
|
||||
|
||||
public StandardNiFiUser(String identity) {
|
||||
this(identity, null);
|
||||
this(identity, null, null);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, String clientAddress) {
|
||||
this(identity, null, clientAddress);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, NiFiUser chain) {
|
||||
this(identity, chain, null);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, NiFiUser chain, String clientAddress) {
|
||||
this.identity = identity;
|
||||
this.chain = chain;
|
||||
this.clientAddress = clientAddress;
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,6 +63,11 @@ public class StandardNiFiUser implements NiFiUser {
|
|||
return this == ANONYMOUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientAddress() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.nifi.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AccessDeniedException;
|
||||
import org.apache.nifi.authorization.AuthorizationRequest;
|
||||
import org.apache.nifi.authorization.AuthorizationResult;
|
||||
|
@ -23,6 +24,7 @@ import org.apache.nifi.authorization.AuthorizationResult.Result;
|
|||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.resource.ResourceType;
|
||||
|
@ -41,7 +43,9 @@ import org.apache.nifi.web.api.dto.ProcessorDTO;
|
|||
import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
|
||||
import org.apache.nifi.web.api.dto.TemplateDTO;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Template implements Authorizable {
|
||||
|
@ -168,7 +172,13 @@ public class Template implements Authorizable {
|
|||
}
|
||||
|
||||
private AuthorizationResult checkAuthorization(final Authorizer authorizer, final RequestAction action, final boolean accessAttempt, final NiFiUser user) {
|
||||
// TODO - include user details context
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
// build the request
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
|
@ -177,6 +187,7 @@ public class Template implements Authorizable {
|
|||
.accessAttempt(accessAttempt)
|
||||
.action(action)
|
||||
.resource(getResource())
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
// perform the authorization
|
||||
|
|
|
@ -14,59 +14,10 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<!--
|
||||
This file lists all authorizations for this NiFi instance. Refer to the properties file and authorizers.xml for configuration details.
|
||||
|
||||
Available resources:
|
||||
/flow - READ - allows user/entity to load the UI and see the flow structure
|
||||
- WRITE - NA
|
||||
/resource - READ - allows user/entity to retrieve the available resources
|
||||
- WRITE - NA
|
||||
/system - READ - allows user/entity to retrieve system level diagnostics (CPU load, disk utilization, etc)
|
||||
- WRITE - NA
|
||||
/controller - READ - allows user/entity to retrieve configuration details for the controller (controller bulletins, thread pool, reporting tasks, etc)
|
||||
- WRITE - allows user/entity to modify configuration details for the controller
|
||||
/provenance - READ - allows user/entity to perform provenance requests. results will be filtered based on access to provenance data per component
|
||||
- WRITE - NA
|
||||
/token - READ - NA
|
||||
- WRITE - allows user/entity to create a token for access the REST API
|
||||
/site-to-site - READ - allows user/entity to retrieve configuration details for performing site to site data transfers with this NiFi
|
||||
- WRITE - NA
|
||||
/proxy - READ - NA
|
||||
- WRITE - allows user/entity to create a proxy request on behalf of another user
|
||||
/process-groups/{id} - READ - allows user/entity to retrieve configuration details for the process group and all descendant components without explicit access policies
|
||||
- WRITE - allows user/entity to create/update/delete configuration details for the process group and all descendant components without explicit access policies
|
||||
/processors/{id} - READ - allows user/entity to retrieve configuration details for the processor overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the processor overriding any inherited authorizations from an ancestor process group
|
||||
/input-ports/{id} - READ - allows user/entity to retrieve configuration details for the input port overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the input port overriding any inherited authorizations from an ancestor process group
|
||||
/output-ports/{id} - READ - allows user/entity to retrieve configuration details for the output port overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the output port overriding any inherited authorizations from an ancestor process group
|
||||
/labels/{id} - READ - allows user/entity to retrieve configuration details for the label overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group
|
||||
/connections/{id} - READ - allows user/entity to retrieve configuration details for the connection overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the label overriding any inherited authorizations from an ancestor process group
|
||||
/remote-process-groups/{id} - READ - allows user/entity to retrieve configuration details for the remote process group overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the remote process group overriding any inherited authorizations from an ancestor process group
|
||||
/templates/{id} - READ - allows user/entity to retrieve configuration details for the template overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to create/update/delete the template overriding any inherited authorizations from an ancestor process group
|
||||
/controller-services/{id} - READ - allows user/entity to retrieve configuration details for the controller service overriding any inherited authorizations from an ancestor process group
|
||||
- WRITE - allows user/entity to update/delete the controller service overriding any inherited authorizations from an ancestor process group
|
||||
/reporting-tasks/{id} - READ - allows user/entity to retrieve configuration details for the reporting tasks overriding any inherited authorizations from the controller
|
||||
- WRITE - allows user/entity to create/update/delete the reporting tasks overriding any inherited authorizations from the controller
|
||||
/{type}/{id}/provenance - READ - allows user/entity to view provenance data from the underlying component
|
||||
- WRITE - NA
|
||||
This file should not be manually edited. Authorizations should only be created through the NiFi UI, and during
|
||||
initial setup of a new instance. Refer to authorizers.xml for properties that allow seeding a new installation
|
||||
with initial authorizations.
|
||||
-->
|
||||
<authorizations>
|
||||
<!--
|
||||
<users>
|
||||
<user identifier="1" identity="" />
|
||||
</users>
|
||||
|
||||
<policies>
|
||||
<policy identifier="1" resource="/flow" action="RW">
|
||||
<user identifier="1" />
|
||||
</policy>
|
||||
</policies>
|
||||
-->
|
||||
|
||||
</authorizations>
|
|
@ -19,11 +19,34 @@
|
|||
must be specified in the nifi.properties file.
|
||||
-->
|
||||
<authorizers>
|
||||
|
||||
<!--
|
||||
The FileAuthorizer is NiFi's provided authorizer and has the following properties:
|
||||
|
||||
- Authorizations File - The file where the FileAuthorizer will store authorizations.
|
||||
|
||||
- Initial Admin Identity - The identity of an initial admin user that will be granted access to the UI and
|
||||
given the ability to create additional users, groups, and policies. The value of this property could be
|
||||
a DN when using certificates or LDAP, or a Kerberos principal. This property will only be used when there
|
||||
are no other users, groups, and policies defined. If this property is specified then a Legacy Authorized
|
||||
Users File can not be specified.
|
||||
|
||||
- Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically
|
||||
converted to the new authorizations model. If this property is specified then an Initial Admin Identity can
|
||||
not be specified, and this property will only be used when there are no other users, groups, and policies defined.
|
||||
|
||||
- Node Identity [unique key] - The identity of a NiFi cluster node. When clustered, a property for each node
|
||||
should be defined, so that every node knows about every other node. If not clustered these properties can be ignored.
|
||||
-->
|
||||
<authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity"></property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
<!--
|
||||
<property name="Node Identity 1"></property>
|
||||
<property name="Node Identity 2"></property>
|
||||
-->
|
||||
</authorizer>
|
||||
</authorizers>
|
|
@ -144,6 +144,16 @@ nifi.security.user.login.identity.provider=${nifi.security.user.login.identity.p
|
|||
nifi.security.ocsp.responder.url=${nifi.security.ocsp.responder.url}
|
||||
nifi.security.ocsp.responder.certificate=${nifi.security.ocsp.responder.certificate}
|
||||
|
||||
# Identity Mapping Properties #
|
||||
# These properties allow normalizing user identities such that identities coming from different identity providers
|
||||
# (certificates, LDAP, Kerberos) can be treated the same internally in NiFi. The following example demonstrates normalizing
|
||||
# DNs from certificates and principals from Kerberos into a common identity string:
|
||||
#
|
||||
# nifi.security.identity.mapping.pattern.dn=^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$
|
||||
# nifi.security.identity.mapping.value.dn=$1@$2
|
||||
# nifi.security.identity.mapping.pattern.kerb=^(.*?)/instance@(.*?)$
|
||||
# nifi.security.identity.mapping.value.kerb=$1@$2
|
||||
|
||||
# cluster common properties (all nodes must have same values) #
|
||||
nifi.cluster.protocol.heartbeat.interval=${nifi.cluster.protocol.heartbeat.interval}
|
||||
nifi.cluster.protocol.is.secure=${nifi.cluster.protocol.is.secure}
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
|
@ -97,12 +98,21 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
|
|||
private void authorizeFlowAccess(final NiFiUser user) {
|
||||
// authorize access
|
||||
serviceFacade.authorizeAccess(lookup -> {
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getFlowResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
|
@ -77,6 +78,8 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
@ -108,12 +111,21 @@ public class AccessResource extends ApplicationResource {
|
|||
* Authorizes access to the flow.
|
||||
*/
|
||||
private boolean hasFlowAccess(final NiFiUser user) {
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getFlowResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
@ -198,7 +210,7 @@ public class AccessResource extends ApplicationResource {
|
|||
// Extract the Base64 encoded token from the Authorization header
|
||||
final String token = StringUtils.substringAfterLast(authorization, " ");
|
||||
|
||||
final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token);
|
||||
final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token, httpServletRequest.getRemoteAddr());
|
||||
final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) jwtAuthenticationProvider.authenticate(jwtRequest);
|
||||
final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser();
|
||||
|
||||
|
@ -215,7 +227,7 @@ public class AccessResource extends ApplicationResource {
|
|||
} else {
|
||||
try {
|
||||
final X509AuthenticationRequestToken x509Request = new X509AuthenticationRequestToken(
|
||||
httpServletRequest.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), principalExtractor, certificates);
|
||||
httpServletRequest.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), principalExtractor, certificates, httpServletRequest.getRemoteAddr());
|
||||
|
||||
final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) x509AuthenticationProvider.authenticate(x509Request);
|
||||
final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser();
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -66,6 +67,8 @@ import javax.ws.rs.core.Context;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RESTful endpoint for managing a Flow Controller.
|
||||
|
@ -92,12 +95,21 @@ public class ControllerResource extends ApplicationResource {
|
|||
private void authorizeController(final RequestAction action) {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getControllerResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.nifi.web.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -40,6 +42,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -84,12 +87,21 @@ public class CountersResource extends ApplicationResource {
|
|||
private void authorizeCounters(final RequestAction action) {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getCountersResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.apache.nifi.authorization.AuthorizationResult.Result;
|
|||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
|
@ -210,12 +211,21 @@ public class FlowResource extends ApplicationResource {
|
|||
private void authorizeFlow(final RequestAction action) {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getFlowResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
@ -228,12 +238,21 @@ public class FlowResource extends ApplicationResource {
|
|||
private boolean isAuthorized(final RequestAction action, final Resource resource) {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(resource)
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(false)
|
||||
.action(action)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -92,12 +93,21 @@ public class ProvenanceResource extends ApplicationResource {
|
|||
private void authorizeProvenanceRequest() {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getProvenanceResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
package org.apache.nifi.web.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -33,6 +35,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -62,12 +65,21 @@ public class ResourceResource extends ApplicationResource {
|
|||
private void authorizeResource() {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getResourceResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.nifi.web.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -36,6 +38,7 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -68,12 +71,21 @@ public class SystemDiagnosticsResource extends ApplicationResource {
|
|||
private void authorizeSystem() {
|
||||
final NiFiUser user = NiFiUserUtils.getNiFiUser();
|
||||
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getSystemResource())
|
||||
.identity(user.getIdentity())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
final AuthorizationResult result = authorizer.authorize(request);
|
||||
|
|
|
@ -1210,7 +1210,8 @@ public class ControllerFacade implements Authorizable {
|
|||
final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
|
||||
for (final String identity : dnChain) {
|
||||
final Authorizable eventAuthorizable = flowController.createProvenanceAuthorizable(componentId);
|
||||
final NiFiUser chainUser = new StandardNiFiUser(identity) {
|
||||
final String clientAddress = user.getIdentity().equals(identity) ? user.getClientAddress() : null;
|
||||
final NiFiUser chainUser = new StandardNiFiUser(identity, clientAddress) {
|
||||
@Override
|
||||
public boolean isAnonymous() {
|
||||
// allow current user to drive anonymous flag as anonymous users are never chained... supports single user case
|
||||
|
@ -1243,7 +1244,8 @@ public class ControllerFacade implements Authorizable {
|
|||
final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
|
||||
for (final String identity : dnChain) {
|
||||
final Authorizable eventAuthorizable = flowController.createProvenanceAuthorizable(componentId);
|
||||
final NiFiUser chainUser = new StandardNiFiUser(identity) {
|
||||
final String clientAddress = user.getIdentity().equals(identity) ? user.getClientAddress() : null;
|
||||
final NiFiUser chainUser = new StandardNiFiUser(identity, clientAddress) {
|
||||
@Override
|
||||
public boolean isAnonymous() {
|
||||
// allow current user to drive anonymous flag as anonymous users are never chained... supports single user case
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
*/
|
||||
package org.apache.nifi.web.dao.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AccessDeniedException;
|
||||
import org.apache.nifi.authorization.AuthorizationRequest;
|
||||
import org.apache.nifi.authorization.AuthorizationResult;
|
||||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
import org.apache.nifi.connectable.Connectable;
|
||||
|
@ -57,6 +59,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -598,6 +601,14 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
|
|||
final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
|
||||
dnChain.forEach(identity -> {
|
||||
// build the request
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(user.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(identity)
|
||||
.anonymous(user.isAnonymous())
|
||||
|
@ -605,6 +616,7 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
|
|||
.action(RequestAction.WRITE)
|
||||
.resource(connection.getResource())
|
||||
.resourceContext(attributes)
|
||||
.userContext(userContext)
|
||||
.build();
|
||||
|
||||
// perform the authorization
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* 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.web.security;
|
||||
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Base AuthenticationProvider that provides common functionality to mapping identities.
|
||||
*/
|
||||
public abstract class NiFiAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NiFiAuthenticationProvider.class);
|
||||
private static final Pattern backReferencePattern = Pattern.compile("\\$(\\d+)");
|
||||
|
||||
private NiFiProperties properties;
|
||||
private List<IdentityMapping> mappings;
|
||||
|
||||
/**
|
||||
* @param properties the NiFiProperties instance
|
||||
*/
|
||||
public NiFiAuthenticationProvider(final NiFiProperties properties) {
|
||||
this.properties = properties;
|
||||
this.mappings = Collections.unmodifiableList(getIdentityMappings(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the identity mappings from NiFiProperties.
|
||||
*
|
||||
* @param properties the NiFiProperties instance
|
||||
* @return a list of identity mappings
|
||||
*/
|
||||
private List<IdentityMapping> getIdentityMappings(final NiFiProperties properties) {
|
||||
final List<IdentityMapping> mappings = new ArrayList<>();
|
||||
|
||||
// go through each property
|
||||
for (String propertyName : properties.stringPropertyNames()) {
|
||||
if (StringUtils.startsWith(propertyName, NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX)) {
|
||||
final String key = StringUtils.substringAfter(propertyName, NiFiProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX);
|
||||
final String identityPattern = properties.getProperty(propertyName);
|
||||
|
||||
if (StringUtils.isBlank(identityPattern)) {
|
||||
LOGGER.warn("Identity Mapping property {} was found, but was empty", new Object[]{propertyName});
|
||||
continue;
|
||||
}
|
||||
|
||||
final String identityValueProperty = NiFiProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX + 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});
|
||||
continue;
|
||||
}
|
||||
|
||||
final IdentityMapping identityMapping = new IdentityMapping(key, Pattern.compile(identityPattern), identityValue);
|
||||
mappings.add(identityMapping);
|
||||
|
||||
LOGGER.debug("Found Identity Mapping with key = {}, pattern = {}, value = {}",
|
||||
new Object[] {key, identityPattern, identityValue});
|
||||
}
|
||||
}
|
||||
|
||||
// sort the list by the key so users can control the ordering in nifi.properties
|
||||
Collections.sort(mappings, new Comparator<IdentityMapping>() {
|
||||
@Override
|
||||
public int compare(IdentityMapping m1, IdentityMapping m2) {
|
||||
return m1.getKey().compareTo(m2.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public List<IdentityMapping> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
protected String mapIdentity(final String identity) {
|
||||
for (IdentityMapping mapping : mappings) {
|
||||
Matcher m = mapping.getPattern().matcher(identity);
|
||||
if (m.matches()) {
|
||||
final String pattern = mapping.getPattern().pattern();
|
||||
final String replacementValue = escapeLiteralBackReferences(mapping.getReplacementValue(), m.groupCount());
|
||||
return identity.replaceAll(pattern, replacementValue);
|
||||
}
|
||||
}
|
||||
|
||||
return identity;
|
||||
}
|
||||
|
||||
// If we find a back reference that is not valid, then we will treat it as a literal string. For example, if we have 3 capturing
|
||||
// groups and the Replacement Value has the value is "I owe $8 to him", then we want to treat the $8 as a literal "$8", rather
|
||||
// than attempting to use it as a back reference.
|
||||
private static String escapeLiteralBackReferences(final String unescaped, final int numCapturingGroups) {
|
||||
if (numCapturingGroups == 0) {
|
||||
return unescaped;
|
||||
}
|
||||
|
||||
String value = unescaped;
|
||||
final Matcher backRefMatcher = backReferencePattern.matcher(value);
|
||||
while (backRefMatcher.find()) {
|
||||
final String backRefNum = backRefMatcher.group(1);
|
||||
if (backRefNum.startsWith("0")) {
|
||||
continue;
|
||||
}
|
||||
final int originalBackRefIndex = Integer.parseInt(backRefNum);
|
||||
int backRefIndex = originalBackRefIndex;
|
||||
|
||||
// if we have a replacement value like $123, and we have less than 123 capturing groups, then
|
||||
// we want to truncate the 3 and use capturing group 12; if we have less than 12 capturing groups,
|
||||
// then we want to truncate the 2 and use capturing group 1; if we don't have a capturing group then
|
||||
// we want to truncate the 1 and get 0.
|
||||
while (backRefIndex > numCapturingGroups && backRefIndex >= 10) {
|
||||
backRefIndex /= 10;
|
||||
}
|
||||
|
||||
if (backRefIndex > numCapturingGroups) {
|
||||
final StringBuilder sb = new StringBuilder(value.length() + 1);
|
||||
final int groupStart = backRefMatcher.start(1);
|
||||
|
||||
sb.append(value.substring(0, groupStart - 1));
|
||||
sb.append("\\");
|
||||
sb.append(value.substring(groupStart - 1));
|
||||
value = sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder to pass around the key, pattern, and replacement from an identity mapping in NiFiProperties.
|
||||
*/
|
||||
public static final class IdentityMapping {
|
||||
|
||||
private final String key;
|
||||
private final Pattern pattern;
|
||||
private final String replacementValue;
|
||||
|
||||
public IdentityMapping(String key, Pattern pattern, String replacementValue) {
|
||||
this.key = key;
|
||||
this.pattern = pattern;
|
||||
this.replacementValue = replacementValue;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Pattern getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public String getReplacementValue() {
|
||||
return replacementValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.web.security;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
|
||||
/**
|
||||
* Base class for authentication request tokens in NiFI.
|
||||
*/
|
||||
public abstract class NiFiAuthenticationRequestToken extends AbstractAuthenticationToken {
|
||||
|
||||
private final String clientAddress;
|
||||
|
||||
/**
|
||||
* @param clientAddress The address of the client making the request
|
||||
*/
|
||||
public NiFiAuthenticationRequestToken(final String clientAddress) {
|
||||
super(null);
|
||||
setAuthenticated(false);
|
||||
this.clientAddress = clientAddress;
|
||||
}
|
||||
|
||||
public String getClientAddress() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
}
|
|
@ -51,7 +51,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
|
|||
} else {
|
||||
// Extract the Base64 encoded token from the Authorization header
|
||||
final String token = StringUtils.substringAfterLast(authorization, " ");
|
||||
return new JwtAuthenticationRequestToken(token);
|
||||
return new JwtAuthenticationRequestToken(token, request.getRemoteAddr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ package org.apache.nifi.web.security.jwt;
|
|||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.InvalidAuthenticationException;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
|
||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
|
@ -30,11 +31,12 @@ import io.jsonwebtoken.JwtException;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
public class JwtAuthenticationProvider implements AuthenticationProvider {
|
||||
public class JwtAuthenticationProvider extends NiFiAuthenticationProvider {
|
||||
|
||||
private final JwtService jwtService;
|
||||
|
||||
public JwtAuthenticationProvider(JwtService jwtService) {
|
||||
public JwtAuthenticationProvider(JwtService jwtService, NiFiProperties nifiProperties) {
|
||||
super(nifiProperties);
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
|
@ -44,7 +46,7 @@ public class JwtAuthenticationProvider implements AuthenticationProvider {
|
|||
|
||||
try {
|
||||
final String jwtPrincipal = jwtService.getAuthenticationFromToken(request.getToken());
|
||||
final NiFiUser user = new StandardNiFiUser(jwtPrincipal);
|
||||
final NiFiUser user = new StandardNiFiUser(mapIdentity(jwtPrincipal), request.getClientAddress());
|
||||
return new NiFiAuthenticationToken(new NiFiUserDetails(user));
|
||||
} catch (JwtException e) {
|
||||
throw new InvalidAuthenticationException(e.getMessage(), e);
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.web.security.jwt;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationRequestToken;
|
||||
|
||||
/**
|
||||
* This is an authentication request with a given JWT token.
|
||||
*/
|
||||
public class JwtAuthenticationRequestToken extends AbstractAuthenticationToken {
|
||||
public class JwtAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
|
||||
|
||||
private final String token;
|
||||
|
||||
|
@ -29,9 +29,10 @@ public class JwtAuthenticationRequestToken extends AbstractAuthenticationToken {
|
|||
* Creates a representation of the jwt authentication request for a user.
|
||||
*
|
||||
* @param token The unique token for this user
|
||||
* @param clientAddress the address of the client making the request
|
||||
*/
|
||||
public JwtAuthenticationRequestToken(final String token) {
|
||||
super(null);
|
||||
public JwtAuthenticationRequestToken(final String token, final String clientAddress) {
|
||||
super(clientAddress);
|
||||
setAuthenticated(false);
|
||||
this.token = token;
|
||||
}
|
||||
|
|
|
@ -56,11 +56,11 @@ public class OtpAuthenticationFilter extends NiFiAuthenticationFilter {
|
|||
if (request.getContextPath().equals("/nifi-api")) {
|
||||
if (isDownloadRequest(request.getPathInfo())) {
|
||||
// handle download requests
|
||||
return new OtpAuthenticationRequestToken(accessToken, true);
|
||||
return new OtpAuthenticationRequestToken(accessToken, true, request.getRemoteAddr());
|
||||
}
|
||||
} else {
|
||||
// handle requests to other context paths (other UI extensions)
|
||||
return new OtpAuthenticationRequestToken(accessToken, false);
|
||||
return new OtpAuthenticationRequestToken(accessToken, false, request.getRemoteAddr());
|
||||
}
|
||||
|
||||
// the path is a support path for otp tokens
|
||||
|
|
|
@ -19,20 +19,22 @@ package org.apache.nifi.web.security.otp;
|
|||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.InvalidAuthenticationException;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
|
||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class OtpAuthenticationProvider implements AuthenticationProvider {
|
||||
public class OtpAuthenticationProvider extends NiFiAuthenticationProvider {
|
||||
|
||||
private OtpService otpService;
|
||||
|
||||
public OtpAuthenticationProvider(OtpService otpService) {
|
||||
public OtpAuthenticationProvider(OtpService otpService, NiFiProperties nifiProperties) {
|
||||
super(nifiProperties);
|
||||
this.otpService = otpService;
|
||||
}
|
||||
|
||||
|
@ -47,7 +49,7 @@ public class OtpAuthenticationProvider implements AuthenticationProvider {
|
|||
} else {
|
||||
otpPrincipal = otpService.getAuthenticationFromUiExtensionToken(request.getToken());
|
||||
}
|
||||
final NiFiUser user = new StandardNiFiUser(otpPrincipal);
|
||||
final NiFiUser user = new StandardNiFiUser(mapIdentity(otpPrincipal), request.getClientAddress());
|
||||
return new NiFiAuthenticationToken(new NiFiUserDetails(user));
|
||||
} catch (OtpAuthenticationException e) {
|
||||
throw new InvalidAuthenticationException(e.getMessage(), e);
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.web.security.otp;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationRequestToken;
|
||||
|
||||
/**
|
||||
* This is an authentication request with a given OTP token.
|
||||
*/
|
||||
public class OtpAuthenticationRequestToken extends AbstractAuthenticationToken {
|
||||
public class OtpAuthenticationRequestToken extends NiFiAuthenticationRequestToken {
|
||||
|
||||
private final String token;
|
||||
private final boolean isDownloadToken;
|
||||
|
@ -30,9 +30,11 @@ public class OtpAuthenticationRequestToken extends AbstractAuthenticationToken {
|
|||
* Creates a representation of the otp authentication request for a user.
|
||||
*
|
||||
* @param token The unique token for this user
|
||||
* @param isDownloadToken Whether or not this represents a download token
|
||||
* @param clientAddress the address of the client making the request
|
||||
*/
|
||||
public OtpAuthenticationRequestToken(final String token, final boolean isDownloadToken) {
|
||||
super(null);
|
||||
public OtpAuthenticationRequestToken(final String token, final boolean isDownloadToken, final String clientAddress) {
|
||||
super(clientAddress);
|
||||
setAuthenticated(false);
|
||||
this.token = token;
|
||||
this.isDownloadToken = isDownloadToken;
|
||||
|
|
|
@ -49,7 +49,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
|
|||
return null;
|
||||
}
|
||||
|
||||
return new X509AuthenticationRequestToken(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), principalExtractor, certificates);
|
||||
return new X509AuthenticationRequestToken(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), principalExtractor, certificates, request.getRemoteAddr());
|
||||
}
|
||||
|
||||
/* setters */
|
||||
|
|
|
@ -23,31 +23,36 @@ import org.apache.nifi.authorization.AuthorizationResult;
|
|||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.UserContextKeys;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.InvalidAuthenticationException;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationProvider;
|
||||
import org.apache.nifi.web.security.ProxiedEntitiesUtils;
|
||||
import org.apache.nifi.web.security.UntrustedProxyException;
|
||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class X509AuthenticationProvider implements AuthenticationProvider {
|
||||
public class X509AuthenticationProvider extends NiFiAuthenticationProvider {
|
||||
|
||||
private X509IdentityProvider certificateIdentityProvider;
|
||||
private Authorizer authorizer;
|
||||
|
||||
public X509AuthenticationProvider(final X509IdentityProvider certificateIdentityProvider, final Authorizer authorizer) {
|
||||
public X509AuthenticationProvider(final X509IdentityProvider certificateIdentityProvider, final Authorizer authorizer, final NiFiProperties nifiProperties) {
|
||||
super(nifiProperties);
|
||||
this.certificateIdentityProvider = certificateIdentityProvider;
|
||||
this.authorizer = authorizer;
|
||||
}
|
||||
|
@ -65,7 +70,8 @@ public class X509AuthenticationProvider implements AuthenticationProvider {
|
|||
}
|
||||
|
||||
if (StringUtils.isBlank(request.getProxiedEntitiesChain())) {
|
||||
return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(authenticationResponse.getIdentity())));
|
||||
final String mappedIdentity = mapIdentity(authenticationResponse.getIdentity());
|
||||
return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(mappedIdentity, request.getClientAddress())));
|
||||
} else {
|
||||
// build the entire proxy chain if applicable - <end-user><proxy1><proxy2>
|
||||
final List<String> proxyChain = new ArrayList<>(ProxiedEntitiesUtils.tokenizeProxiedEntitiesChain(request.getProxiedEntitiesChain()));
|
||||
|
@ -74,7 +80,7 @@ public class X509AuthenticationProvider implements AuthenticationProvider {
|
|||
// add the chain as appropriate to each proxy
|
||||
NiFiUser proxy = null;
|
||||
for (final ListIterator<String> chainIter = proxyChain.listIterator(proxyChain.size()); chainIter.hasPrevious();) {
|
||||
final String identity = chainIter.previous();
|
||||
final String identity = mapIdentity(chainIter.previous());
|
||||
|
||||
if (chainIter.hasPrevious()) {
|
||||
// authorize this proxy in order to authenticate this user
|
||||
|
@ -84,6 +90,7 @@ public class X509AuthenticationProvider implements AuthenticationProvider {
|
|||
.accessAttempt(true)
|
||||
.action(RequestAction.WRITE)
|
||||
.resource(ResourceFactory.getProxyResource())
|
||||
.userContext(proxy == null ? getUserContext(request) : null) // only set the context for the real user
|
||||
.build();
|
||||
|
||||
final AuthorizationResult proxyAuthorizationResult = authorizer.authorize(proxyAuthorizationRequest);
|
||||
|
@ -92,13 +99,29 @@ public class X509AuthenticationProvider implements AuthenticationProvider {
|
|||
}
|
||||
}
|
||||
|
||||
proxy = new StandardNiFiUser(chainIter.previous(), proxy);
|
||||
// only set the client address for user making the request, we don't know the client address of the proxies
|
||||
if (proxy == null) {
|
||||
proxy = new StandardNiFiUser(identity, proxy, request.getClientAddress());
|
||||
} else {
|
||||
proxy = new StandardNiFiUser(identity, proxy, null);
|
||||
}
|
||||
}
|
||||
|
||||
return new NiFiAuthenticationToken(new NiFiUserDetails(proxy));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String,String> getUserContext(final X509AuthenticationRequestToken request) {
|
||||
final Map<String,String> userContext;
|
||||
if (!StringUtils.isBlank(request.getClientAddress())) {
|
||||
userContext = new HashMap<>();
|
||||
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), request.getClientAddress());
|
||||
} else {
|
||||
userContext = null;
|
||||
}
|
||||
return userContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return X509AuthenticationRequestToken.class.isAssignableFrom(authentication);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package org.apache.nifi.web.security.x509;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.apache.nifi.web.security.NiFiAuthenticationRequestToken;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
@ -25,7 +25,7 @@ import java.security.cert.X509Certificate;
|
|||
/**
|
||||
* This is an authentication request with a given JWT token.
|
||||
*/
|
||||
public class X509AuthenticationRequestToken extends AbstractAuthenticationToken {
|
||||
public class X509AuthenticationRequestToken extends NiFiAuthenticationRequestToken {
|
||||
|
||||
private final String proxiedEntitiesChain;
|
||||
private final X509PrincipalExtractor principalExtractor;
|
||||
|
@ -37,8 +37,8 @@ public class X509AuthenticationRequestToken extends AbstractAuthenticationToken
|
|||
* @param proxiedEntitiesChain The http servlet request
|
||||
* @param certificates The certificate chain
|
||||
*/
|
||||
public X509AuthenticationRequestToken(final String proxiedEntitiesChain, final X509PrincipalExtractor principalExtractor, final X509Certificate[] certificates) {
|
||||
super(null);
|
||||
public X509AuthenticationRequestToken(final String proxiedEntitiesChain, final X509PrincipalExtractor principalExtractor, final X509Certificate[] certificates, final String clientAddress) {
|
||||
super(clientAddress);
|
||||
setAuthenticated(false);
|
||||
this.proxiedEntitiesChain = proxiedEntitiesChain;
|
||||
this.principalExtractor = principalExtractor;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<bean id="x509AuthenticationProvider" class="org.apache.nifi.web.security.x509.X509AuthenticationProvider">
|
||||
<constructor-arg ref="certificateIdentityProvider" index="0"/>
|
||||
<constructor-arg ref="authorizer" index="1"/>
|
||||
<constructor-arg ref="nifiProperties"/>
|
||||
</bean>
|
||||
|
||||
<!-- jwt service -->
|
||||
|
@ -53,6 +54,7 @@
|
|||
<!-- jwt authentication provider -->
|
||||
<bean id="jwtAuthenticationProvider" class="org.apache.nifi.web.security.jwt.JwtAuthenticationProvider">
|
||||
<constructor-arg ref="jwtService"/>
|
||||
<constructor-arg ref="nifiProperties"/>
|
||||
</bean>
|
||||
|
||||
<!-- otp service -->
|
||||
|
@ -61,6 +63,7 @@
|
|||
<!-- otp authentication provider -->
|
||||
<bean id="otpAuthenticationProvider" class="org.apache.nifi.web.security.otp.OtpAuthenticationProvider">
|
||||
<constructor-arg ref="otpService"/>
|
||||
<constructor-arg ref="nifiProperties"/>
|
||||
</bean>
|
||||
|
||||
<!-- Kerberos service -->
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* 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.web.security;
|
||||
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class NiFiAuthenticationProviderTest {
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesProvided() {
|
||||
final String pattern = "^cn=(.*?),dc=(.*?),dc=(.*?)$";
|
||||
final String value = "$1@$2.$3";
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", pattern);
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn", value);
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
|
||||
TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
List<NiFiAuthenticationProvider.IdentityMapping> mappings = provider.getMappings();
|
||||
assertEquals(1, mappings.size());
|
||||
assertEquals("dn", mappings.get(0).getKey());
|
||||
assertEquals(pattern, mappings.get(0).getPattern().pattern());
|
||||
assertEquals(value, mappings.get(0).getReplacementValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMappings() {
|
||||
Properties properties = new Properties();
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
|
||||
TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
List<NiFiAuthenticationProvider.IdentityMapping> mappings = provider.getMappings();
|
||||
assertEquals(0, mappings.size());
|
||||
|
||||
final String identity = "john";
|
||||
assertEquals(identity, provider.mapIdentity(identity));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatternPropertyWithNoValue() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", "");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn", "value");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
|
||||
TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
List<NiFiAuthenticationProvider.IdentityMapping> mappings = provider.getMappings();
|
||||
assertEquals(0, mappings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatternPropertyWithNoCorrespondingValueProperty() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", "");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
|
||||
TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
List<NiFiAuthenticationProvider.IdentityMapping> mappings = provider.getMappings();
|
||||
assertEquals(0, mappings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleMappings() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.1", "pattern1");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.1", "value1");
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.2", "pattern2");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.2", "value2");
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.3", "pattern3");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.3", "value3");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
|
||||
TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
List<NiFiAuthenticationProvider.IdentityMapping> mappings = provider.getMappings();
|
||||
assertEquals(3, mappings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapIdentityWithSingleMapping() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", "^cn=(.*?),dc=(.*?),dc=(.*?)$");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn", "$1@$2.$3");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
final TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
|
||||
final String identity = "cn=jsmith,dc=aaa,dc=bbb";
|
||||
final String mappedIdentity = provider.mapIdentity(identity);
|
||||
assertEquals("jsmith@aaa.bbb", mappedIdentity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapIdentityWithIncorrectGroupReference() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", "^cn=(.*?),dc=(.*?),dc=(.*?)$");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn", "$1@$2.$4");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
final TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
|
||||
final String identity = "cn=jsmith,dc=aaa,dc=bbb";
|
||||
final String mappedIdentity = provider.mapIdentity(identity);
|
||||
assertEquals("jsmith@aaa.$4", mappedIdentity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapIdentityWithNoGroupReference() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn", "^cn=(.*?),dc=(.*?),dc=(.*?)$");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn", "this makes no sense");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
final TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
|
||||
final String identity = "cn=jsmith,dc=aaa,dc=bbb";
|
||||
final String mappedIdentity = provider.mapIdentity(identity);
|
||||
assertEquals("this makes no sense", mappedIdentity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapIdentityWithMultipleMatchingPatterns() {
|
||||
// create two pattern properties that are the same, but the value properties are different
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn2", "^cn=(.*?),dc=(.*?),dc=(.*?)$");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn2", "$1_$2_$3");
|
||||
properties.setProperty("nifi.security.identity.mapping.pattern.dn1", "^cn=(.*?),dc=(.*?),dc=(.*?)$");
|
||||
properties.setProperty("nifi.security.identity.mapping.value.dn1", "$1 $2 $3");
|
||||
|
||||
final NiFiProperties nifiProperties = getNiFiProperties(properties);
|
||||
final TestableNiFiAuthenticationProvider provider = new TestableNiFiAuthenticationProvider(nifiProperties);
|
||||
|
||||
// the mapping should always use dn1 because it is sorted
|
||||
final String identity = "cn=jsmith,dc=aaa,dc=bbb";
|
||||
final String mappedIdentity = provider.mapIdentity(identity);
|
||||
assertEquals("jsmith aaa bbb", mappedIdentity);
|
||||
}
|
||||
|
||||
private NiFiProperties getNiFiProperties(final Properties properties) {
|
||||
final NiFiProperties nifiProperties = Mockito.mock(NiFiProperties.class);
|
||||
when(nifiProperties.stringPropertyNames()).thenReturn(properties.stringPropertyNames());
|
||||
|
||||
when(nifiProperties.getProperty(anyString())).then(new Answer<String>() {
|
||||
@Override
|
||||
public String answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return properties.getProperty((String)invocationOnMock.getArguments()[0]);
|
||||
}
|
||||
});
|
||||
return nifiProperties;
|
||||
}
|
||||
|
||||
private static class TestableNiFiAuthenticationProvider extends NiFiAuthenticationProvider {
|
||||
/**
|
||||
* @param properties the NiFiProperties instance
|
||||
*/
|
||||
public TestableNiFiAuthenticationProvider(NiFiProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,9 +17,11 @@
|
|||
package org.apache.nifi.web.security.otp;
|
||||
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
|
@ -41,6 +43,7 @@ public class OtpAuthenticationProviderTest {
|
|||
|
||||
private OtpService otpService;
|
||||
private OtpAuthenticationProvider otpAuthenticationProvider;
|
||||
private NiFiProperties nifiProperties;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -72,12 +75,12 @@ public class OtpAuthenticationProviderTest {
|
|||
}
|
||||
}).when(otpService).getAuthenticationFromUiExtensionToken(anyString());
|
||||
|
||||
otpAuthenticationProvider = new OtpAuthenticationProvider(otpService);
|
||||
otpAuthenticationProvider = new OtpAuthenticationProvider(otpService, Mockito.mock(NiFiProperties.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUiExtensionPath() throws Exception {
|
||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(UI_EXTENSION_TOKEN, false);
|
||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(UI_EXTENSION_TOKEN, false, null);
|
||||
|
||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
||||
|
@ -89,7 +92,7 @@ public class OtpAuthenticationProviderTest {
|
|||
|
||||
@Test
|
||||
public void testDownload() throws Exception {
|
||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(DOWNLOAD_TOKEN, true);
|
||||
final OtpAuthenticationRequestToken request = new OtpAuthenticationRequestToken(DOWNLOAD_TOKEN, true, null);
|
||||
|
||||
final NiFiAuthenticationToken result = (NiFiAuthenticationToken) otpAuthenticationProvider.authenticate(request);
|
||||
final NiFiUserDetails details = (NiFiUserDetails) result.getPrincipal();
|
||||
|
|
|
@ -1743,6 +1743,12 @@ public class TestPersistentProvenanceRepository {
|
|||
public boolean isAnonymous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -201,6 +201,11 @@ public class TestVolatileProvenanceRepository {
|
|||
public boolean isAnonymous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientAddress() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue