Repository: nifi-registry
Updated Branches:
refs/heads/master 2f7523538 -> 870b94787
NIFIREG-32: Add delete as available action for access policy management.
- Add 'delete' as available action for authorization access policies, which are (resource, action) pairs.
- Add unit tests for authorization classes.
- Add BadRequestExceptionMapper.
- Move StandardManagedAuthorizer from nifi-registry-security-api-imple to nifi-registry-framework.
- Add missing default constructor for User and UserGroup
This closes #18.
Signed-off-by: Bryan Bende <bbende@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/870b9478
Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/870b9478
Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/870b9478
Branch: refs/heads/master
Commit: 870b94787aa907ba4a85e3c7b32cd84dfd897fc7
Parents: 2f75235
Author: Kevin Doran <kdoran.apache@gmail.com>
Authored: Wed Oct 11 09:44:45 2017 -0400
Committer: Bryan Bende <bbende@apache.org>
Committed: Thu Oct 12 15:35:10 2017 -0400
----------------------------------------------------------------------
.../nifi/registry/model/authorization/User.java | 2 +
.../registry/model/authorization/UserGroup.java | 2 +
.../StandardManagedAuthorizer.java | 264 ++++++++
.../authorization/resource/Authorizable.java | 2 +-
.../registry/service/AuthorizationService.java | 15 +-
.../service/AuthorizationServiceSpec.groovy | 658 +++++++++++++++++++
.../AbstractPolicyBasedAuthorizer.java | 2 +
.../StandardManagedAuthorizer.java | 264 --------
.../file/AuthorizationsHolder.java | 2 +
.../file/FileAccessPolicyProvider.java | 14 +-
.../src/main/xsd/authorizations.xsd | 1 +
.../registry/authorization/RequestAction.java | 10 +-
.../registry/web/api/AccessPolicyResource.java | 4 +-
.../registry/web/api/BucketFlowResource.java | 2 +-
.../nifi/registry/web/api/BucketResource.java | 2 +-
.../nifi/registry/web/api/TenantResource.java | 4 +-
.../web/mapper/BadRequestExceptionMapper.java | 47 ++
.../mapper/IllegalArgumentExceptionMapper.java | 2 +-
18 files changed, 1012 insertions(+), 285 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
index a15cbc7..76ed867 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
@@ -29,6 +29,8 @@ public class User extends Tenant {
private Set<Tenant> userGroups;
private Set<AccessPolicySummary> accessPolicies;
+ public User() {}
+
public User(String identifier, String identity) {
super(identifier, identity);
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
index d504e66..51b1297 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
@@ -32,6 +32,8 @@ public class UserGroup extends Tenant {
private Set<Tenant> users;
private Set<AccessPolicySummary> accessPolicies;
+ public UserGroup() {}
+
public UserGroup(String identifier, String identity) {
super(identifier, identity);
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
new file mode 100644
index 0000000..8cd4fea
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
@@ -0,0 +1,264 @@
+/*
+ * 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.registry.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException;
+import org.apache.nifi.registry.util.PropertyValue;
+import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Set;
+
+public class StandardManagedAuthorizer implements ManagedAuthorizer {
+
+ private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+ private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
+
+ private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider";
+ private static final String ACCESS_POLICY_PROVIDER_ELEMENT = "accessPolicyProvider";
+
+ private AccessPolicyProviderLookup accessPolicyProviderLookup;
+ private AccessPolicyProvider accessPolicyProvider;
+ private UserGroupProvider userGroupProvider;
+
+ public StandardManagedAuthorizer() {}
+
+ // exposed for testing to inject mocks
+ public StandardManagedAuthorizer(AccessPolicyProvider accessPolicyProvider, UserGroupProvider userGroupProvider) {
+ this.accessPolicyProvider = accessPolicyProvider;
+ this.userGroupProvider = userGroupProvider;
+ }
+
+ @Override
+ public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
+ accessPolicyProviderLookup = initializationContext.getAccessPolicyProviderLookup();
+ }
+
+ @Override
+ public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+ final PropertyValue accessPolicyProviderKey = configurationContext.getProperty("Access Policy Provider");
+ if (!accessPolicyProviderKey.isSet()) {
+ throw new AuthorizerCreationException("The Access Policy Provider must be set.");
+ }
+
+ accessPolicyProvider = accessPolicyProviderLookup.getAccessPolicyProvider(accessPolicyProviderKey.getValue());
+
+ // ensure the desired access policy provider was found
+ if (accessPolicyProvider == null) {
+ throw new AuthorizerCreationException(String.format("Unable to locate configured Access Policy Provider: %s", accessPolicyProviderKey));
+ }
+
+ userGroupProvider = accessPolicyProvider.getUserGroupProvider();
+
+ // ensure the desired access policy provider has a user group provider
+ if (userGroupProvider == null) {
+ throw new AuthorizerCreationException(String.format("Configured Access Policy Provider %s does not contain a User Group Provider", accessPolicyProviderKey));
+ }
+ }
+
+ @Override
+ public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
+ final String resourceIdentifier = request.getResource().getIdentifier();
+ final AccessPolicy policy = accessPolicyProvider.getAccessPolicy(resourceIdentifier, request.getAction());
+ if (policy == null) {
+ return AuthorizationResult.resourceNotFound();
+ }
+
+ final UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(request.getIdentity());
+
+ final User user = userAndGroups.getUser();
+ if (user == null) {
+ return AuthorizationResult.denied(String.format("Unknown user with identity '%s'.", request.getIdentity()));
+ }
+
+ final Set<Group> userGroups = userAndGroups.getGroups();
+ if (policy.getUsers().contains(user.getIdentifier()) || containsGroup(userGroups, policy)) {
+ return AuthorizationResult.approved();
+ }
+
+ return AuthorizationResult.denied(request.getExplanationSupplier().get());
+ }
+
+ /**
+ * 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 == null || userGroups.isEmpty() || policy.getGroups().isEmpty()) {
+ return false;
+ }
+
+ for (Group userGroup : userGroups) {
+ if (policy.getGroups().contains(userGroup.getIdentifier())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getFingerprint() throws AuthorizationAccessException {
+ XMLStreamWriter writer = null;
+ final StringWriter out = new StringWriter();
+ try {
+ writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+ writer.writeStartDocument();
+ writer.writeStartElement("managedAuthorizations");
+
+ writer.writeStartElement(ACCESS_POLICY_PROVIDER_ELEMENT);
+ if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
+ writer.writeCharacters(((ConfigurableAccessPolicyProvider) accessPolicyProvider).getFingerprint());
+ }
+ writer.writeEndElement();
+
+ writer.writeStartElement(USER_GROUP_PROVIDER_ELEMENT);
+ if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
+ writer.writeCharacters(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint());
+ }
+ writer.writeEndElement();
+
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.flush();
+ } catch (XMLStreamException e) {
+ throw new AuthorizationAccessException("Unable to generate fingerprint", e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (XMLStreamException e) {
+ // nothing to do here
+ }
+ }
+ }
+
+ return out.toString();
+ }
+
+ @Override
+ public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+ if (StringUtils.isBlank(fingerprint)) {
+ return;
+ }
+
+ final FingerprintHolder fingerprintHolder = parseFingerprint(fingerprint);
+
+ if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint()) && accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
+ ((ConfigurableAccessPolicyProvider) accessPolicyProvider).inheritFingerprint(fingerprintHolder.getPolicyFingerprint());
+ }
+
+ if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint()) && userGroupProvider instanceof ConfigurableUserGroupProvider) {
+ ((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(fingerprintHolder.getUserGroupFingerprint());
+ }
+ }
+
+ @Override
+ public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+ final FingerprintHolder fingerprintHolder = parseFingerprint(proposedFingerprint);
+
+ if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint())) {
+ if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
+ ((ConfigurableAccessPolicyProvider) accessPolicyProvider).checkInheritability(fingerprintHolder.getPolicyFingerprint());
+ } else {
+ throw new UninheritableAuthorizationsException("Policy fingerprint is not blank and the configured AccessPolicyProvider does not support fingerprinting.");
+ }
+ }
+
+ if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint())) {
+ if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
+ ((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(fingerprintHolder.getUserGroupFingerprint());
+ } else {
+ throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting.");
+ }
+ }
+ }
+
+ private final FingerprintHolder parseFingerprint(final String fingerprint) throws AuthorizationAccessException {
+ final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
+
+ try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
+ final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
+ final Document document = docBuilder.parse(in);
+ final Element rootElement = document.getDocumentElement();
+
+ final NodeList accessPolicyProviderList = rootElement.getElementsByTagName(ACCESS_POLICY_PROVIDER_ELEMENT);
+ if (accessPolicyProviderList.getLength() != 1) {
+ throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", ACCESS_POLICY_PROVIDER_ELEMENT, fingerprint));
+ }
+
+ final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT);
+ if (userGroupProviderList.getLength() != 1) {
+ throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint));
+ }
+
+ final Node accessPolicyProvider = accessPolicyProviderList.item(0);
+ final Node userGroupProvider = userGroupProviderList.item(0);
+ return new FingerprintHolder(accessPolicyProvider.getTextContent(), userGroupProvider.getTextContent());
+ } catch (SAXException | ParserConfigurationException | IOException e) {
+ throw new AuthorizationAccessException("Unable to parse fingerprint", e);
+ }
+ }
+
+ @Override
+ public AccessPolicyProvider getAccessPolicyProvider() {
+ return accessPolicyProvider;
+ }
+
+ @Override
+ public void preDestruction() throws AuthorizerDestructionException {
+
+ }
+
+ private static class FingerprintHolder {
+ private final String policyFingerprint;
+ private final String userGroupFingerprint;
+
+ public FingerprintHolder(String policyFingerprint, String userGroupFingerprint) {
+ this.policyFingerprint = policyFingerprint;
+ this.userGroupFingerprint = userGroupFingerprint;
+ }
+
+ public String getPolicyFingerprint() {
+ return policyFingerprint;
+ }
+
+ public String getUserGroupFingerprint() {
+ return userGroupFingerprint;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
index 3e694e7..77f0fec 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
@@ -112,7 +112,7 @@ public interface Authorizable {
if (RequestAction.READ.equals(action)) {
safeDescription.append("view ");
} else {
- safeDescription.append("modify ");
+ safeDescription.append("modify "); // covers write or delete
}
safeDescription.append(resource.getSafeDescription()).append(".");
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index cc63af0..48a7ae3 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -51,6 +51,7 @@ import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
@@ -197,7 +198,7 @@ public class AuthorizationService {
}
}
- public List<UserGroup> getUserGroupsForUser(String userIdentifier) {
+ private List<UserGroup> getUserGroupsForUser(String userIdentifier) {
this.readLock.lock();
try {
return userGroupProvider.getGroups()
@@ -306,7 +307,7 @@ public class AuthorizationService {
}
}
- public List<AccessPolicySummary> getAccessPolicySummariesForUser(String userIdentifier) {
+ private List<AccessPolicySummary> getAccessPolicySummariesForUser(String userIdentifier) {
readLock.lock();
try {
return accessPolicyProvider.getAccessPolicies().stream()
@@ -318,7 +319,7 @@ public class AuthorizationService {
}
}
- public List<AccessPolicySummary> getAccessPolicySummariesForUserGroup(String userGroupIdentifier) {
+ private List<AccessPolicySummary> getAccessPolicySummariesForUserGroup(String userGroupIdentifier) {
readLock.lock();
try {
return accessPolicyProvider.getAccessPolicies().stream()
@@ -340,9 +341,9 @@ public class AuthorizationService {
accessPolicy.setResource(currentAccessPolicy.getResource());
accessPolicy.setAction(currentAccessPolicy.getAction().toString());
- org.apache.nifi.registry.authorization.AccessPolicy updateAccessPolicy =
+ org.apache.nifi.registry.authorization.AccessPolicy updatedAccessPolicy =
((ConfigurableAccessPolicyProvider) accessPolicyProvider).updateAccessPolicy(accessPolicyFromDTO(accessPolicy));
- return accessPolicyToDTO(updateAccessPolicy);
+ return accessPolicyToDTO(updatedAccessPolicy);
} finally {
writeLock.unlock();
}
@@ -465,9 +466,9 @@ public class AuthorizationService {
}
Collection<Tenant> users = accessPolicy.getUsers() != null
- ? accessPolicy.getUsers().stream().map(this::getTenant).collect(Collectors.toList()) : null;
+ ? accessPolicy.getUsers().stream().map(this::getTenant).filter(Objects::nonNull).collect(Collectors.toList()) : null;
Collection<Tenant> userGroups = accessPolicy.getGroups() != null
- ? accessPolicy.getGroups().stream().map(this::getTenant).collect(Collectors.toList()) : null;
+ ? accessPolicy.getGroups().stream().map(this::getTenant).filter(Objects::nonNull).collect(Collectors.toList()) : null;
Boolean isConfigurable = AuthorizerCapabilityDetection.isAccessPolicyConfigurable(authorizer, accessPolicy);
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
new file mode 100644
index 0000000..3e99483
--- /dev/null
+++ b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -0,0 +1,658 @@
+/*
+ * 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.registry.service
+
+import org.apache.nifi.registry.authorization.AccessPolicy as AuthAccessPolicy
+import org.apache.nifi.registry.authorization.AuthorizableLookup
+import org.apache.nifi.registry.authorization.ConfigurableAccessPolicyProvider
+import org.apache.nifi.registry.authorization.ConfigurableUserGroupProvider
+import org.apache.nifi.registry.authorization.Group
+import org.apache.nifi.registry.authorization.RequestAction
+import org.apache.nifi.registry.authorization.StandardManagedAuthorizer
+import org.apache.nifi.registry.authorization.User as AuthUser
+import org.apache.nifi.registry.authorization.exception.AccessDeniedException
+import org.apache.nifi.registry.authorization.resource.Authorizable
+import org.apache.nifi.registry.authorization.resource.ResourceType
+import org.apache.nifi.registry.bucket.Bucket
+import org.apache.nifi.registry.model.authorization.AccessPolicy
+import org.apache.nifi.registry.model.authorization.User
+import org.apache.nifi.registry.model.authorization.UserGroup
+import spock.lang.Specification
+
+class AuthorizationServiceSpec extends Specification {
+
+ def registryService = Mock(RegistryService)
+ def authorizableLookup = Mock(AuthorizableLookup)
+ def userGroupProvider = Mock(ConfigurableUserGroupProvider)
+ def accessPolicyProvider = Mock(ConfigurableAccessPolicyProvider)
+
+ AuthorizationService authorizationService
+
+ def setup() {
+ accessPolicyProvider.getUserGroupProvider() >> userGroupProvider
+ def authorizer = new StandardManagedAuthorizer(accessPolicyProvider, userGroupProvider)
+ authorizationService = new AuthorizationService(authorizableLookup, authorizer, registryService)
+ }
+
+ // ----- Tenant tests -----------------------------------------------------
+
+ def "get tenant"() {
+
+ when: "get tenant for existing user identifier"
+ userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ def userResult = authorizationService.getTenant("userId")
+
+ then: "user with identifier is returned as DTO"
+ with(userResult) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+
+ when: "get tenant for existing group identifier"
+ userGroupProvider.getGroup("groupId") >> new Group.Builder().identifier("groupId").name("groupname").build()
+ def groupResult = authorizationService.getTenant("groupId")
+
+ then: "group with identifier is returned as DTO"
+ with(groupResult) {
+ identifier == "groupId"
+ identity == "groupname"
+ }
+
+
+ when: "get tenant for non-existent identifier"
+ userGroupProvider.getUser("id") >> null
+ userGroupProvider.getGroup("id") >> null
+ def result = authorizationService.getTenant("id")
+
+ then: "null is returned"
+ result == null
+
+ }
+
+ // ----- User tests -------------------------------------------------------
+
+ def "create user"() {
+
+ setup:
+ userGroupProvider.addUser(!null as AuthUser) >> {
+ AuthUser u -> new AuthUser.Builder().identifier(u.identifier).identity(u.identity).build()
+ }
+ userGroupProvider.getGroups() >> new HashSet<Group>() // needed for converting user to DTO
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting user to DTO
+
+ when: "new user is created successfully"
+ def user = new User(null, "username")
+ User createdUser = authorizationService.createUser(user)
+
+ then: "created user has been assigned an identifier"
+ with(createdUser) {
+ identifier != null
+ identity == "username"
+ }
+
+ }
+
+ def "list users"() {
+
+ setup:
+ userGroupProvider.getUsers() >> [
+ new AuthUser.Builder().identifier("user1").identity("username1").build(),
+ new AuthUser.Builder().identifier("user2").identity("username2").build(),
+ new AuthUser.Builder().identifier("user3").identity("username3").build(),
+ ]
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+ when: "list of users is queried"
+ def users = authorizationService.getUsers()
+
+ then: "users are successfully returned as list of DTO objects"
+ users != null
+ users.size() == 3
+ with(users[0]) {
+ identifier == "user1"
+ identity == "username1"
+ }
+ with(users[1]) {
+ identifier == "user2"
+ identity == "username2"
+ }
+ with(users[2]) {
+ identifier == "user3"
+ identity == "username3"
+ }
+
+ }
+
+ def "get user"() {
+
+ setup:
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "get user for existing user identifier"
+ userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ def user1 = authorizationService.getUser("userId")
+
+ then: "user is returned converted to DTO"
+ with(user1) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+
+ when: "get user for non-existent user identifier"
+ userGroupProvider.getUser("nonExistentUserId") >> null
+ userGroupProvider.getGroup("nonExistentUserId") >> null
+ def user2 = authorizationService.getUser("nonExistentUserId")
+
+ then: "no user is returned"
+ user2 == null
+
+ }
+
+ def "update user"() {
+
+ setup:
+ userGroupProvider.updateUser(!null as AuthUser) >> {
+ AuthUser u -> new AuthUser.Builder().identifier(u.identifier).identity(u.identity).build()
+ }
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "user is updated"
+ def user = authorizationService.updateUser(new User("userId", "username"))
+
+ then: "updated user is returned"
+ with(user) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+ }
+
+ def "delete user"() {
+
+ setup:
+ userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ userGroupProvider.deleteUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "user is deleted"
+ def user = authorizationService.deleteUser("userId")
+
+ then: "deleted user is returned converted to DTO"
+ with(user) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+ }
+
+ // ----- User Group tests -------------------------------------------------
+
+ def "create user group"() {
+
+ setup:
+ userGroupProvider.addGroup(!null as Group) >> {
+ Group g -> new Group.Builder().identifier(g.identifier).name(g.name).build()
+ }
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting to DTO
+
+ when: "new group is created successfully"
+ def group = new UserGroup(null, "groupName")
+ UserGroup createdGroup = authorizationService.createUserGroup(group)
+
+ then: "created group has been assigned an identifier"
+ with(createdGroup) {
+ identifier != null
+ identity == "groupName"
+ }
+
+ }
+
+ def "list user groups"() {
+
+ setup:
+ userGroupProvider.getGroups() >> [
+ new Group.Builder().identifier("groupId1").name("groupName1").build(),
+ new Group.Builder().identifier("groupId2").name("groupName2").build(),
+ new Group.Builder().identifier("groupId3").name("groupName3").build(),
+ ]
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+ when: "list of groups is queried"
+ def groups = authorizationService.getUserGroups()
+
+ then: "groups are successfully returned as list of DTO objects"
+ groups != null
+ groups.size() == 3
+ with(groups[0]) {
+ identifier == "groupId1"
+ identity == "groupName1"
+ }
+ with(groups[1]) {
+ identifier == "groupId2"
+ identity == "groupName2"
+ }
+ with(groups[2]) {
+ identifier == "groupId3"
+ identity == "groupName3"
+ }
+
+ }
+
+ def "get user group"() {
+
+ setup:
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "get group for existing user identifier"
+ userGroupProvider.getGroup("groupId") >> new Group.Builder().identifier("groupId").name ("groupName").build()
+ def g1 = authorizationService.getUserGroup("groupId")
+
+ then: "group is returned converted to DTO"
+ with(g1) {
+ identifier == "groupId"
+ identity == "groupName"
+ }
+
+
+ when: "get group for non-existent group identifier"
+ userGroupProvider.getUser("nonExistentId") >> null
+ userGroupProvider.getGroup("nonExistentId") >> null
+ def g2 = authorizationService.getUserGroup("nonExistentId")
+
+ then: "no group is returned"
+ g2 == null
+
+ }
+
+ def "update user group"() {
+
+ setup:
+ userGroupProvider.updateGroup(!null as Group) >> {
+ Group g -> new Group.Builder().identifier(g.identifier).name(g.name).build()
+ }
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "group is updated"
+ def group = authorizationService.updateUserGroup(new UserGroup("id", "name"))
+
+ then: "updated group is returned converted to DTO"
+ with(group) {
+ identifier == "id"
+ identity == "name"
+ }
+
+ }
+
+ def "delete user group"() {
+
+ setup:
+ userGroupProvider.getGroup("id") >> new Group.Builder().identifier("id").name("name").build()
+ userGroupProvider.deleteGroup("id") >> new Group.Builder().identifier("id").name("name").build()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "group is deleted"
+ def group = authorizationService.deleteUserGroup("id")
+
+ then: "deleted user is returned"
+ with(group) {
+ identifier == "id"
+ identity == "name"
+ }
+
+ }
+
+ // ----- Access Policy tests ----------------------------------------------
+
+ def "create access policy"() {
+
+ setup:
+ accessPolicyProvider.addAccessPolicy(!null as AuthAccessPolicy) >> {
+ AuthAccessPolicy p -> new AuthAccessPolicy.Builder()
+ .identifier(p.identifier)
+ .resource(p.resource)
+ .action(p.action)
+ .addGroups(p.groups)
+ .addUsers(p.users)
+ .build()
+ }
+ accessPolicyProvider.isConfigurable(_ as AuthAccessPolicy) >> true
+
+
+ when: "new access policy is created successfully"
+ def createdPolicy = authorizationService.createAccessPolicy(new AccessPolicy([resource: "/resource", action: "read"]))
+
+ then: "created policy has been assigned an identifier"
+ with(createdPolicy) {
+ identifier != null
+ resource == "/resource"
+ action == "read"
+ configurable == true
+ }
+
+ }
+
+ def "list access policies"() {
+
+ setup:
+ accessPolicyProvider.getAccessPolicies() >> [
+ new AuthAccessPolicy.Builder().identifier("ap1").resource("r1").action(RequestAction.READ).build(),
+ new AuthAccessPolicy.Builder().identifier("ap2").resource("r2").action(RequestAction.WRITE).build()
+ ]
+
+ when: "list access polices is queried"
+ def policies = authorizationService.getAccessPolicies()
+
+ then: "access policies are successfully returned as list of DTO objects"
+ policies != null
+ policies.size() == 2
+ with(policies[0]) {
+ identifier == "ap1"
+ resource == "r1"
+ action == RequestAction.READ.toString()
+ }
+ with(policies[1]) {
+ identifier == "ap2"
+ resource == "r2"
+ action == RequestAction.WRITE.toString()
+ }
+
+ }
+
+ def "get access policy"() {
+
+ when: "get policy for existing identifier"
+ accessPolicyProvider.getAccessPolicy("id") >> new AuthAccessPolicy.Builder()
+ .identifier("id")
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .build()
+ def p1 = authorizationService.getAccessPolicy("id")
+
+ then: "policy is returned converted to DTO"
+ with(p1) {
+ identifier == "id"
+ resource == "/resource"
+ action == RequestAction.READ.toString()
+ }
+
+
+ when: "get policy for non-existent identifier"
+ accessPolicyProvider.getAccessPolicy("nonExistentId") >> null
+ def p2 = authorizationService.getAccessPolicy("nonExistentId")
+
+ then: "no policy is returned"
+ p2 == null
+
+ }
+
+
+ def "update access policy"() {
+
+ setup:
+ def users = [
+ "user1": "alice",
+ "user2": "bob",
+ "user3": "charlie" ]
+ def groups = [
+ "group1": "users",
+ "group2": "devs",
+ "group3": "admins" ]
+ def policies = [
+ "policy1": [
+ "resource": "/resource1",
+ "action": "read",
+ "users": [ "user1" ],
+ "groups": []
+ ]
+ ]
+ def mapDtoUser = { String id -> new User(id, users[id])}
+ def mapDtoGroup = { String id -> new UserGroup(id, groups[id])}
+ def mapAuthUser = { String id -> new AuthUser.Builder().identifier(id).identity(users[id]).build() }
+ def mapAuthGroup = { String id -> new Group.Builder().identifier(id).name(groups[id]).build() }
+ def mapAuthAccessPolicy = {
+ String id -> return new AuthAccessPolicy.Builder()
+ .identifier(id)
+ .resource(policies[id]["resource"] as String)
+ .action(RequestAction.valueOfValue(policies[id]["action"] as String))
+ .addUsers(policies[id]["users"] as Set<String>)
+ .addGroups(policies[id]["groups"] as Set<String>)
+ .build()
+ }
+ userGroupProvider.getUser(!null as String) >> { String id -> users.containsKey(id) ? mapAuthUser(id) : null }
+ userGroupProvider.getGroup(!null as String) >> { String id -> groups.containsKey(id) ? mapAuthGroup(id) : null }
+ userGroupProvider.getUsers() >> {
+ def authUsers = []
+ users.each{ k, v -> authUsers.add(new AuthUser.Builder().identifier(k).identity(v).build()) }
+ return authUsers
+ }
+ userGroupProvider.getGroups() >> {
+ def authGroups = []
+ users.each{ k, v -> authGroups.add(new Group.Builder().identifier(k).name(v).build()) }
+ return authGroups
+ }
+ accessPolicyProvider.getAccessPolicy(!null as String) >> { String id -> policies.containsKey(id) ? mapAuthAccessPolicy(id) : null }
+ accessPolicyProvider.updateAccessPolicy(!null as AuthAccessPolicy) >> {
+ AuthAccessPolicy p -> new AuthAccessPolicy.Builder()
+ .identifier(p.identifier)
+ .resource(p.resource)
+ .action(p.action)
+ .addGroups(p.groups)
+ .addUsers(p.users)
+ .build()
+ }
+ accessPolicyProvider.isConfigurable(_ as AuthAccessPolicy) >> true
+
+
+ when: "policy is updated"
+ def policy = new AccessPolicy([identifier: "policy1", resource: "/resource1", action: "read"])
+ policy.addUsers([mapDtoUser("user1"), mapDtoUser("user2")])
+ policy.addUserGroups([mapDtoGroup("group1")])
+ def p1 = authorizationService.updateAccessPolicy(policy)
+
+ then: "updated group is returned converted to DTO"
+ p1 != null
+ p1.users.size() == 2
+ def sortedUsers = p1.users.sort{it.identifier}
+ with(sortedUsers[0]) {
+ identifier == "user1"
+ identity == "alice"
+ }
+ with(sortedUsers[1]) {
+ identifier == "user2"
+ identity == "bob"
+ }
+ p1.userGroups.size() == 1
+ with(p1.userGroups[0]) {
+ identifier == "group1"
+ identity == "users"
+ }
+
+
+ when: "attempt to change policy resource and action"
+ def p2 = authorizationService.updateAccessPolicy(new AccessPolicy([identifier: "policy1", resource: "/newResource", action: "write"]))
+
+ then: "resource and action are unchanged"
+ with(p2) {
+ identifier == "policy1"
+ resource == "/resource1"
+ action == "read"
+ }
+
+ }
+
+ def "delete access policy"() {
+
+ setup:
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ userGroupProvider.getUsers() >> new HashSet<AuthUser>()
+ accessPolicyProvider.getAccessPolicy("id") >> {
+ String id -> new AuthAccessPolicy.Builder()
+ .identifier("id")
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .addGroups(new HashSet<String>())
+ .addUsers(new HashSet<String>())
+ .build()
+ }
+ accessPolicyProvider.deleteAccessPolicy(!null as String) >> {
+ String id -> new AuthAccessPolicy.Builder()
+ .identifier(id)
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .addGroups(new HashSet<String>())
+ .addUsers(new HashSet<String>())
+ .build()
+ }
+
+ when: "access policy is deleted"
+ def policy = authorizationService.deleteAccessPolicy("id")
+
+ then: "deleted policy is returned"
+ with(policy) {
+ identifier == "id"
+ resource == "/resource"
+ action == RequestAction.READ.toString()
+ }
+
+ }
+
+ // ----- Resource tests ---------------------------------------------------
+
+ def "get resources"() {
+
+ setup:
+ def buckets = [
+ "b1": [
+ "name": "Bucket #1",
+ "description": "An initial bucket for testing",
+ "createdTimestamp": 1
+ ],
+ "b2": [
+ "name": "Bucket #2",
+ "description": "A second bucket for testing",
+ "createdTimestamp": 2
+ ],
+ ]
+ def mapBucket = {
+ String id -> new Bucket([
+ identifier: id,
+ name: buckets[id]["name"] as String,
+ description: buckets[id]["description"] as String]) }
+
+ registryService.getBuckets() >> {[ mapBucket("b1"), mapBucket("b2") ]}
+
+ when:
+ def resources = authorizationService.getResources()
+
+ then:
+ resources != null
+ resources.size() == 7
+ def sortedResources = resources.sort{it.identifier}
+ sortedResources[0].identifier == "/buckets"
+ sortedResources[1].identifier == "/buckets/b1"
+ sortedResources[2].identifier == "/buckets/b2"
+ sortedResources[3].identifier == "/policies"
+ sortedResources[4].identifier == "/proxy"
+ sortedResources[5].identifier == "/resources"
+ sortedResources[6].identifier == "/tenants"
+
+ }
+
+ def "get authorized resources"() {
+
+ setup:
+ def buckets = [
+ "b1": [
+ "name": "Bucket #1",
+ "description": "An initial bucket for testing",
+ "createdTimestamp": 1
+ ],
+ "b2": [
+ "name": "Bucket #2",
+ "description": "A second bucket for testing",
+ "createdTimestamp": 2
+ ],
+ ]
+ def mapBucket = {
+ String id -> new Bucket([
+ identifier: id,
+ name: buckets[id]["name"] as String,
+ description: buckets[id]["description"] as String]) }
+
+ registryService.getBuckets() >> {[ mapBucket("b1"), mapBucket("b2") ]}
+
+ def authorized = Mock(Authorizable)
+ authorized.authorize(_, _, _) >> { return }
+ def denied = Mock(Authorizable)
+ denied.authorize(_, _, _) >> { throw new AccessDeniedException("") }
+
+ authorizableLookup.getAuthorizableByResource("/buckets") >> authorized
+ authorizableLookup.getAuthorizableByResource("/buckets/b1") >> authorized
+ authorizableLookup.getAuthorizableByResource("/buckets/b2") >> denied
+ authorizableLookup.getAuthorizableByResource("/policies") >> authorized
+ authorizableLookup.getAuthorizableByResource("/proxy") >> denied
+ authorizableLookup.getAuthorizableByResource("/resources") >> authorized
+ authorizableLookup.getAuthorizableByResource("/tenants") >> authorized
+
+
+ when:
+ def resources = authorizationService.getAuthorizedResources(RequestAction.READ)
+
+ then:
+ resources != null
+ resources.size() == 5
+ def sortedResources = resources.sort{it.identifier}
+ sortedResources[0].identifier == "/buckets"
+ sortedResources[1].identifier == "/buckets/b1"
+ sortedResources[2].identifier == "/policies"
+ sortedResources[3].identifier == "/resources"
+ sortedResources[4].identifier == "/tenants"
+
+
+ when:
+ def filteredResources = authorizationService.getAuthorizedResources(RequestAction.READ, ResourceType.Bucket)
+
+ then:
+ filteredResources != null
+ filteredResources.size() == 2
+ def sortedFilteredResources = filteredResources.sort{it.identifier}
+ sortedFilteredResources[0].identifier == "/buckets"
+ sortedFilteredResources[1].identifier == "/buckets/b1"
+
+ }
+
+
+
+
+
+
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
index 5313911..718ecc7 100644
--- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
+++ b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
@@ -468,6 +468,8 @@ public abstract class AbstractPolicyBasedAuthorizer implements ManagedAuthorizer
builder.action(RequestAction.READ);
} else if (actions.equals(RequestAction.WRITE.name())) {
builder.action(RequestAction.WRITE);
+ } else if (actions.equals(RequestAction.DELETE.name())) {
+ builder.action(RequestAction.DELETE);
} else {
throw new IllegalStateException("Unknown Policy Action: " + actions);
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
deleted file mode 100644
index 8cd4fea..0000000
--- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.registry.authorization;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException;
-import org.apache.nifi.registry.util.PropertyValue;
-import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
-import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
-import org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Set;
-
-public class StandardManagedAuthorizer implements ManagedAuthorizer {
-
- private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
- private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
-
- private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider";
- private static final String ACCESS_POLICY_PROVIDER_ELEMENT = "accessPolicyProvider";
-
- private AccessPolicyProviderLookup accessPolicyProviderLookup;
- private AccessPolicyProvider accessPolicyProvider;
- private UserGroupProvider userGroupProvider;
-
- public StandardManagedAuthorizer() {}
-
- // exposed for testing to inject mocks
- public StandardManagedAuthorizer(AccessPolicyProvider accessPolicyProvider, UserGroupProvider userGroupProvider) {
- this.accessPolicyProvider = accessPolicyProvider;
- this.userGroupProvider = userGroupProvider;
- }
-
- @Override
- public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
- accessPolicyProviderLookup = initializationContext.getAccessPolicyProviderLookup();
- }
-
- @Override
- public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
- final PropertyValue accessPolicyProviderKey = configurationContext.getProperty("Access Policy Provider");
- if (!accessPolicyProviderKey.isSet()) {
- throw new AuthorizerCreationException("The Access Policy Provider must be set.");
- }
-
- accessPolicyProvider = accessPolicyProviderLookup.getAccessPolicyProvider(accessPolicyProviderKey.getValue());
-
- // ensure the desired access policy provider was found
- if (accessPolicyProvider == null) {
- throw new AuthorizerCreationException(String.format("Unable to locate configured Access Policy Provider: %s", accessPolicyProviderKey));
- }
-
- userGroupProvider = accessPolicyProvider.getUserGroupProvider();
-
- // ensure the desired access policy provider has a user group provider
- if (userGroupProvider == null) {
- throw new AuthorizerCreationException(String.format("Configured Access Policy Provider %s does not contain a User Group Provider", accessPolicyProviderKey));
- }
- }
-
- @Override
- public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
- final String resourceIdentifier = request.getResource().getIdentifier();
- final AccessPolicy policy = accessPolicyProvider.getAccessPolicy(resourceIdentifier, request.getAction());
- if (policy == null) {
- return AuthorizationResult.resourceNotFound();
- }
-
- final UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(request.getIdentity());
-
- final User user = userAndGroups.getUser();
- if (user == null) {
- return AuthorizationResult.denied(String.format("Unknown user with identity '%s'.", request.getIdentity()));
- }
-
- final Set<Group> userGroups = userAndGroups.getGroups();
- if (policy.getUsers().contains(user.getIdentifier()) || containsGroup(userGroups, policy)) {
- return AuthorizationResult.approved();
- }
-
- return AuthorizationResult.denied(request.getExplanationSupplier().get());
- }
-
- /**
- * 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 == null || userGroups.isEmpty() || policy.getGroups().isEmpty()) {
- return false;
- }
-
- for (Group userGroup : userGroups) {
- if (policy.getGroups().contains(userGroup.getIdentifier())) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public String getFingerprint() throws AuthorizationAccessException {
- XMLStreamWriter writer = null;
- final StringWriter out = new StringWriter();
- try {
- writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
- writer.writeStartDocument();
- writer.writeStartElement("managedAuthorizations");
-
- writer.writeStartElement(ACCESS_POLICY_PROVIDER_ELEMENT);
- if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
- writer.writeCharacters(((ConfigurableAccessPolicyProvider) accessPolicyProvider).getFingerprint());
- }
- writer.writeEndElement();
-
- writer.writeStartElement(USER_GROUP_PROVIDER_ELEMENT);
- if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
- writer.writeCharacters(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint());
- }
- writer.writeEndElement();
-
- writer.writeEndElement();
- writer.writeEndDocument();
- writer.flush();
- } catch (XMLStreamException e) {
- throw new AuthorizationAccessException("Unable to generate fingerprint", e);
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (XMLStreamException e) {
- // nothing to do here
- }
- }
- }
-
- return out.toString();
- }
-
- @Override
- public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
- if (StringUtils.isBlank(fingerprint)) {
- return;
- }
-
- final FingerprintHolder fingerprintHolder = parseFingerprint(fingerprint);
-
- if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint()) && accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
- ((ConfigurableAccessPolicyProvider) accessPolicyProvider).inheritFingerprint(fingerprintHolder.getPolicyFingerprint());
- }
-
- if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint()) && userGroupProvider instanceof ConfigurableUserGroupProvider) {
- ((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(fingerprintHolder.getUserGroupFingerprint());
- }
- }
-
- @Override
- public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
- final FingerprintHolder fingerprintHolder = parseFingerprint(proposedFingerprint);
-
- if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint())) {
- if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
- ((ConfigurableAccessPolicyProvider) accessPolicyProvider).checkInheritability(fingerprintHolder.getPolicyFingerprint());
- } else {
- throw new UninheritableAuthorizationsException("Policy fingerprint is not blank and the configured AccessPolicyProvider does not support fingerprinting.");
- }
- }
-
- if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint())) {
- if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
- ((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(fingerprintHolder.getUserGroupFingerprint());
- } else {
- throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting.");
- }
- }
- }
-
- private final FingerprintHolder parseFingerprint(final String fingerprint) throws AuthorizationAccessException {
- final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
-
- try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
- final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
- final Document document = docBuilder.parse(in);
- final Element rootElement = document.getDocumentElement();
-
- final NodeList accessPolicyProviderList = rootElement.getElementsByTagName(ACCESS_POLICY_PROVIDER_ELEMENT);
- if (accessPolicyProviderList.getLength() != 1) {
- throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", ACCESS_POLICY_PROVIDER_ELEMENT, fingerprint));
- }
-
- final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT);
- if (userGroupProviderList.getLength() != 1) {
- throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint));
- }
-
- final Node accessPolicyProvider = accessPolicyProviderList.item(0);
- final Node userGroupProvider = userGroupProviderList.item(0);
- return new FingerprintHolder(accessPolicyProvider.getTextContent(), userGroupProvider.getTextContent());
- } catch (SAXException | ParserConfigurationException | IOException e) {
- throw new AuthorizationAccessException("Unable to parse fingerprint", e);
- }
- }
-
- @Override
- public AccessPolicyProvider getAccessPolicyProvider() {
- return accessPolicyProvider;
- }
-
- @Override
- public void preDestruction() throws AuthorizerDestructionException {
-
- }
-
- private static class FingerprintHolder {
- private final String policyFingerprint;
- private final String userGroupFingerprint;
-
- public FingerprintHolder(String policyFingerprint, String userGroupFingerprint) {
- this.policyFingerprint = policyFingerprint;
- this.userGroupFingerprint = userGroupFingerprint;
- }
-
- public String getPolicyFingerprint() {
- return policyFingerprint;
- }
-
- public String getUserGroupFingerprint() {
- return userGroupFingerprint;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java
index 0c219d3..38a1571 100644
--- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java
+++ b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java
@@ -101,6 +101,8 @@ public class AuthorizationsHolder {
builder.action(RequestAction.READ);
} else if (authorizationCode.equals(FileAccessPolicyProvider.WRITE_CODE)){
builder.action(RequestAction.WRITE);
+ } else if (authorizationCode.equals(FileAccessPolicyProvider.DELETE_CODE)){
+ builder.action(RequestAction.DELETE);
} else {
throw new IllegalStateException("Unknown Policy Action: " + authorizationCode);
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java
index d17edec..50184dd 100644
--- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java
+++ b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java
@@ -105,21 +105,24 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
private static final String RESOURCE_ATTR = "resource";
private static final String ACTIONS_ATTR = "actions";
+ /* These codes must match the enumeration values set in authorizations.xsd */
static final String READ_CODE = "R";
static final String WRITE_CODE = "W";
- /* TODO, add DELETE_CODE */
+ static final String DELETE_CODE = "D";
/* TODO - move this somewhere into nifi-registry-security-framework so it can be applied to any ConfigurableAccessPolicyProvider
* (and also gets us away from requiring magic strings here) */
private static final ResourceActionPair[] INITIAL_ADMIN_ACCESS_POLICIES = {
new ResourceActionPair("/resources", READ_CODE),
- new ResourceActionPair("/resources", WRITE_CODE),
new ResourceActionPair("/tenants", READ_CODE),
new ResourceActionPair("/tenants", WRITE_CODE),
+ new ResourceActionPair("/tenants", DELETE_CODE),
new ResourceActionPair("/policies", READ_CODE),
new ResourceActionPair("/policies", WRITE_CODE),
+ new ResourceActionPair("/policies", DELETE_CODE),
new ResourceActionPair("/buckets", READ_CODE),
new ResourceActionPair("/buckets", WRITE_CODE),
+ new ResourceActionPair("/buckets", DELETE_CODE),
};
static final String PROP_NODE_IDENTITY_PREFIX = "Node Identity ";
@@ -406,6 +409,8 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
builder.action(RequestAction.READ);
} else if (actions.equals(RequestAction.WRITE.name())) {
builder.action(RequestAction.WRITE);
+ } else if (actions.equals(RequestAction.DELETE.name())) {
+ builder.action(RequestAction.DELETE);
} else {
throw new IllegalStateException("Unknown Policy Action: " + actions);
}
@@ -566,6 +571,8 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
builder.action(RequestAction.READ);
} else if (action.equals(WRITE_CODE)) {
builder.action(RequestAction.WRITE);
+ } else if (action.equals(DELETE_CODE)) {
+ builder.action(RequestAction.DELETE);
} else {
throw new IllegalStateException("Unknown Policy Action: " + action);
}
@@ -593,6 +600,9 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide
case WRITE:
policy.setAction(WRITE_CODE);
break;
+ case DELETE:
+ policy.setAction(DELETE_CODE);
+ break;
default:
break;
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd b/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd
index 0ab27a9..2c8f805 100644
--- a/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd
+++ b/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd
@@ -63,6 +63,7 @@
<xs:restriction base="xs:string">
<xs:enumeration value="R"/>
<xs:enumeration value="W"/>
+ <xs:enumeration value="D"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
index 231b9eb..a489ecc 100644
--- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
+++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java
@@ -23,8 +23,8 @@ import java.util.StringJoiner;
*/
public enum RequestAction {
READ("read"),
- WRITE("write");
- // TODO, add DELETE RequestAction feature
+ WRITE("write"),
+ DELETE("delete");
private String value;
@@ -38,10 +38,12 @@ public enum RequestAction {
}
public static RequestAction valueOfValue(final String action) {
- if (RequestAction.READ.toString().equals(action)) {
+ if (RequestAction.READ.toString().equalsIgnoreCase(action)) {
return RequestAction.READ;
- } else if (RequestAction.WRITE.toString().equals(action)) {
+ } else if (RequestAction.WRITE.toString().equalsIgnoreCase(action)) {
return RequestAction.WRITE;
+ } else if (RequestAction.DELETE.toString().equalsIgnoreCase(action)) {
+ return RequestAction.DELETE;
} else {
StringJoiner stringJoiner = new StringJoiner(", ");
for(RequestAction ra : RequestAction.values()) {
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
index d72aa94..013d9c9 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
@@ -204,7 +204,7 @@ public class AccessPolicyResource extends AuthorizableApplicationResource {
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getAccessPolicyForResource(
- @ApiParam(value = "The request action.", allowableValues = "read, write" /* todo, +delete */, required = true)
+ @ApiParam(value = "The request action.", allowableValues = "read, write, delete", required = true)
@PathParam("action")
final String action,
@ApiParam(value = "The resource of the policy.", required = true)
@@ -300,7 +300,7 @@ public class AccessPolicyResource extends AuthorizableApplicationResource {
final String identifier) {
verifyAuthorizerSupportsConfigurablePolicies();
- authorizeAccessToPolicy(RequestAction.WRITE, identifier);
+ authorizeAccessToPolicy(RequestAction.DELETE, identifier);
AccessPolicy deletedPolicy = authorizationService.deleteAccessPolicy(identifier);
return generateOkResponse(deletedPolicy).build();
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
index a42a739..0bf5b48 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
@@ -211,7 +211,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource {
@PathParam("bucketId") final String bucketId,
@PathParam("flowId") final String flowId) {
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
+ authorizeBucketAccess(RequestAction.DELETE, bucketId);
final VersionedFlow deletedFlow = registryService.deleteFlow(bucketId, flowId);
return Response.status(Response.Status.OK).entity(deletedFlow).build();
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
index 907a63a..86c3262 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
@@ -217,7 +217,7 @@ public class BucketResource extends AuthorizableApplicationResource {
if (StringUtils.isBlank(bucketId)) {
throw new BadRequestException("Bucket id cannot be blank");
}
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
+ authorizeBucketAccess(RequestAction.DELETE, bucketId);
final Bucket deletedBucket = registryService.deleteBucket(bucketId);
return Response.status(Response.Status.OK).entity(deletedBucket).build();
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
index 6b02938..f6fd15f 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
@@ -257,7 +257,7 @@ public class TenantResource extends AuthorizableApplicationResource {
@PathParam("id") final String identifier) {
verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.WRITE);
+ authorizeAccess(RequestAction.DELETE);
final User user = authorizationService.deleteUser(identifier);
return generateOkResponse(user).build();
@@ -449,7 +449,7 @@ public class TenantResource extends AuthorizableApplicationResource {
@PathParam("id")
final String identifier) {
verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.WRITE);
+ authorizeAccess(RequestAction.DELETE);
final UserGroup userGroup = authorizationService.deleteUserGroup(identifier);
return generateOkResponse(userGroup).build();
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/BadRequestExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/BadRequestExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/BadRequestExceptionMapper.java
new file mode 100644
index 0000000..7be2a36
--- /dev/null
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/BadRequestExceptionMapper.java
@@ -0,0 +1,47 @@
+/*
+ * 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.registry.web.mapper;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps exceptions into client responses.
+ */
+@Provider
+public class BadRequestExceptionMapper implements ExceptionMapper<BadRequestException> {
+
+ private static final Logger logger = LoggerFactory.getLogger(BadRequestExceptionMapper.class);
+
+ @Override
+ public Response toResponse(BadRequestException exception) {
+ logger.info(String.format("%s. Returning %s response.", exception, Response.Status.BAD_REQUEST));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(StringUtils.EMPTY, exception);
+ }
+
+ return Response.status(Response.Status.BAD_REQUEST).entity(exception.getMessage()).type("text/plain").build();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/870b9478/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalArgumentExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalArgumentExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalArgumentExceptionMapper.java
index 3ae44cf..f00cc7d 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalArgumentExceptionMapper.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalArgumentExceptionMapper.java
@@ -25,7 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Maps resource not found exceptions into client responses.
+ * Maps exceptions into client responses.
*/
@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
|