nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bbe...@apache.org
Subject nifi-registry git commit: NIFIREG-32: Add delete as available action for access policy management.
Date Thu, 12 Oct 2017 19:35:28 GMT
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> {


Mime
View raw message