NIFIREG-9: Initial Auth Implementation

Authentication and authorization enforcement for web API, largely based on NiFi.
This commit adds interfaces, framework, and file-based authorizer providers 
(file access policy provider, file user group provider).
Authentication of identities is currently based on certificates in two-way SSL 
(HTTPS). Alternative identity strategies (user&pass, JWT) will
be added later building upon the foundation in this commit.

As part of this feature, some changes were made to the RegistryService 
interface and the providers it utilizes.

This closes #14.

Signed-off-by: Bryan Bende <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/785cb81f
Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/785cb81f
Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/785cb81f

Branch: refs/heads/master
Commit: 785cb81ff0b7cab1f083f1b8dfb643a8561ec566
Parents: a87d42e
Author: Kevin Doran <[email protected]>
Authored: Mon Oct 2 22:40:26 2017 -0400
Committer: Bryan Bende <[email protected]>
Committed: Thu Oct 5 14:08:59 2017 -0400

----------------------------------------------------------------------
 nifi-registry-assembly/pom.xml                  |   9 +-
 nifi-registry-data-model/pom.xml                |   4 +
 .../model/authorization/AccessPolicy.java       |  72 ++
 .../authorization/AccessPolicySummary.java      |  72 ++
 .../model/authorization/AccessStatus.java       |  69 ++
 .../registry/model/authorization/Resource.java  |  56 ++
 .../registry/model/authorization/Tenant.java    |  61 ++
 .../nifi/registry/model/authorization/User.java |  78 ++
 .../registry/model/authorization/UserGroup.java |  83 ++
 nifi-registry-framework/pom.xml                 |  72 +-
 .../authorization/AuthorizableLookup.java       |  75 ++
 .../registry/authorization/AuthorizeAccess.java |  21 +
 .../AuthorizerCapabilityDetection.java          |  75 ++
 .../authorization/AuthorizerFactory.java        |  33 +
 .../AuthorizerFactoryException.java             |  33 +
 .../CompositeConfigurableUserGroupProvider.java | 197 +++++
 .../CompositeUserGroupProvider.java             | 177 ++++
 .../StandardAuthorizableLookup.java             | 218 +++++
 .../StandardAuthorizerFactory.java              | 796 ++++++++++++++++++
 .../resource/AccessPolicyAuthorizable.java      | 122 +++
 .../authorization/resource/Authorizable.java    | 300 +++++++
 ...rcePolicyPermissionsThroughBaseResource.java |  36 +
 .../authorization/resource/ResourceFactory.java | 261 ++++++
 .../authorization/resource/ResourceType.java    |  52 ++
 .../registry/authorization/user/NiFiUser.java   |  52 ++
 .../authorization/user/NiFiUserDetails.java     |  91 ++
 .../authorization/user/NiFiUserUtils.java       |  91 ++
 .../authorization/user/StandardNiFiUser.java    | 189 +++++
 .../registry/db/DatabaseMetadataService.java    |  92 ++-
 .../db/repository/BucketItemRepository.java     |  27 +
 .../db/repository/BucketRepository.java         |   3 +
 .../registry/db/repository/FlowRepository.java  |  30 +
 .../exception/AdministrationException.java      |  39 +
 .../registry/service/AuthorizationService.java  | 695 ++++++++++++++++
 .../nifi/registry/service/MetadataService.java  |  24 +-
 .../nifi/registry/service/RegistryService.java  |  77 +-
 .../service/params/QueryParameters.java         |   2 +
 .../src/main/xsd/authorizers.xsd                |  67 ++
 .../registry/service/TestRegistryService.java   |  60 +-
 nifi-registry-jetty/pom.xml                     |   5 -
 .../properties/NiFiRegistryProperties.java      |  43 +-
 .../properties/util/IdentityMapping.java        |  48 ++
 .../properties/util/IdentityMappingUtil.java    | 145 ++++
 nifi-registry-provider-api/pom.xml              |   6 -
 .../main/resources/conf/authorized-users.xml    |  20 -
 .../src/main/resources/conf/authorizers.xml     | 141 ++++
 .../resources/conf/nifi-registry.properties     |   3 +-
 nifi-registry-security-api-impl/pom.xml         |  97 +++
 .../AbstractPolicyBasedAuthorizer.java          | 822 +++++++++++++++++++
 .../StandardAuthorizerConfigurationContext.java |  54 ++
 ...StandardAuthorizerInitializationContext.java |  55 ++
 .../StandardManagedAuthorizer.java              | 264 ++++++
 .../authorization/UsersAndAccessPolicies.java   |  52 ++
 .../annotation/AuthorizerContext.java           |  35 +
 .../file/AuthorizationsHolder.java              | 185 +++++
 .../file/FileAccessPolicyProvider.java          | 746 +++++++++++++++++
 .../authorization/file/FileAuthorizer.java      | 288 +++++++
 .../file/FileUserGroupProvider.java             | 775 +++++++++++++++++
 .../authorization/file/IdentifierUtil.java      |  35 +
 .../authorization/file/UserGroupHolder.java     | 241 ++++++
 .../src/main/xsd/authorizations.xsd             |  86 ++
 .../src/main/xsd/tenants.xsd                    |  96 +++
 nifi-registry-security-api/pom.xml              |  38 +
 .../registry/authorization/AccessPolicy.java    | 367 +++++++++
 .../authorization/AccessPolicyProvider.java     |  90 ++
 ...cessPolicyProviderInitializationContext.java |  30 +
 .../AccessPolicyProviderLookup.java             |  31 +
 .../authorization/AuthorizationAuditor.java     |  30 +
 .../authorization/AuthorizationRequest.java     | 245 ++++++
 .../authorization/AuthorizationResult.java      | 103 +++
 .../nifi/registry/authorization/Authorizer.java |  63 ++
 .../AuthorizerConfigurationContext.java         |  48 ++
 .../AuthorizerInitializationContext.java        |  30 +
 .../authorization/AuthorizerLookup.java         |  31 +
 .../ConfigurableAccessPolicyProvider.java       | 108 +++
 .../ConfigurableUserGroupProvider.java          | 163 ++++
 .../nifi/registry/authorization/Group.java      | 263 ++++++
 .../authorization/ManagedAuthorizer.java        |  59 ++
 .../registry/authorization/RequestAction.java   |  54 ++
 .../nifi/registry/authorization/Resource.java   |  44 +
 .../nifi/registry/authorization/User.java       | 188 +++++
 .../registry/authorization/UserAndGroups.java   |  40 +
 .../registry/authorization/UserContextKeys.java |  26 +
 .../authorization/UserGroupProvider.java        | 108 +++
 .../UserGroupProviderInitializationContext.java |  37 +
 .../authorization/UserGroupProviderLookup.java  |  31 +
 .../exception/AccessDeniedException.java        |  39 +
 .../exception/AuthorizationAccessException.java |  32 +
 .../exception/AuthorizerCreationException.java  |  39 +
 .../AuthorizerDestructionException.java         |  39 +
 .../UninheritableAuthorizationsException.java   |  28 +
 nifi-registry-security/pom.xml                  |  70 --
 .../security/AuthorizationProvider.java         |  88 --
 .../registry/security/AuthorizedUserFilter.java |  87 --
 nifi-registry-security/src/main/xsd/users.xsd   |  37 -
 .../org/apache/nifi/registry/util/DataUnit.java | 245 ++++++
 .../apache/nifi/registry/util/FileUtils.java    | 175 ++++
 .../apache/nifi/registry/util/FormatUtils.java  | 261 ++++++
 .../nifi/registry/util/PropertyValue.java       |  91 ++
 .../registry/util/StandardPropertyValue.java    |  79 ++
 nifi-registry-web-api/pom.xml                   |  29 +-
 .../web/NiFiRegistryResourceConfig.java         |  26 +-
 .../web/NiFiRegistrySecurityConfig.java         | 220 +++++
 .../registry/web/api/AccessPolicyResource.java  | 344 ++++++++
 .../nifi/registry/web/api/AccessResource.java   | 164 ++++
 .../registry/web/api/ApplicationResource.java   | 175 ++++
 .../api/AuthorizableApplicationResource.java    |  82 ++
 .../registry/web/api/BucketFlowResource.java    | 359 +++++++-
 .../nifi/registry/web/api/BucketResource.java   |  79 +-
 .../nifi/registry/web/api/FlowResource.java     | 219 +----
 .../registry/web/api/HttpStatusMessages.java    |  30 +
 .../nifi/registry/web/api/ItemResource.java     |  24 +-
 .../nifi/registry/web/api/ResourceResource.java |  91 ++
 .../nifi/registry/web/api/TenantResource.java   | 486 +++++++++++
 .../link/builder/VersionedFlowLinkBuilder.java  |   5 +-
 .../VersionedFlowSnapshotLinkBuilder.java       |   3 +-
 .../web/mapper/AccessDeniedExceptionMapper.java |  73 ++
 .../mapper/AdministrationExceptionMapper.java   |  44 +
 ...ationCredentialsNotFoundExceptionMapper.java |  48 ++
 .../AuthorizationAccessExceptionMapper.java     |  44 +
 .../InvalidAuthenticationExceptionMapper.java   |  45 +
 .../web/mapper/NotFoundExceptionMapper.java     |  48 ++
 .../web/response/AuthenticationResponse.java    |  65 ++
 .../InvalidAuthenticationException.java         |  35 +
 .../web/security/NiFiAnonymousUserFilter.java   |  40 +
 .../web/security/NiFiAuthenticationFilter.java  | 154 ++++
 .../security/NiFiAuthenticationProvider.java    |  84 ++
 .../NiFiAuthenticationRequestToken.java         |  41 +
 .../web/security/ProxiedEntitiesUtils.java      | 163 ++++
 .../web/security/UntrustedProxyException.java   |  34 +
 .../token/LoginAuthenticationToken.java         | 123 +++
 .../security/token/NiFiAuthenticationToken.java |  55 ++
 .../security/token/OtpAuthenticationToken.java  |  56 ++
 .../web/security/util/CertificateUtils.java     | 668 +++++++++++++++
 .../web/security/util/KeyStoreUtils.java        |  82 ++
 .../web/security/util/KeystoreType.java         |  25 +
 .../x509/SubjectDnX509PrincipalExtractor.java   |  33 +
 .../security/x509/X509AuthenticationFilter.java |  64 ++
 .../x509/X509AuthenticationProvider.java        | 165 ++++
 .../x509/X509AuthenticationRequestToken.java    |  75 ++
 .../security/x509/X509CertificateExtractor.java |  53 ++
 .../security/x509/X509CertificateValidator.java |  47 ++
 .../web/security/x509/X509IdentityProvider.java |  95 +++
 .../nifi/registry/web/link/TestLinkService.java |  13 +-
 pom.xml                                         |   7 +-
 145 files changed, 16468 insertions(+), 637 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-assembly/pom.xml b/nifi-registry-assembly/pom.xml
index dbed960..7de9b89 100644
--- a/nifi-registry-assembly/pom.xml
+++ b/nifi-registry-assembly/pom.xml
@@ -97,6 +97,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-security-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
             <artifactId>nifi-registry-web-ui</artifactId>
             <type>war</type>
             <version>0.0.1-SNAPSHOT</version>
@@ -135,8 +140,8 @@
         <nifi.registry.security.truststore />
         <nifi.registry.security.truststoreType />
         <nifi.registry.security.truststorePasswd />
-        <nifi.registry.security.needClientAuth />
-        
<nifi.registry.security.authorized.users>./conf/authorized-users.xml</nifi.registry.security.authorized.users>
+        
<nifi.registry.security.authorizers.configuration.file>./conf/authorizers.xml</nifi.registry.security.authorizers.configuration.file><nifi.registry.security.needClientAuth
 />
+        
<nifi.registry.security.authorizer>managed-authorizer</nifi.registry.security.authorizer>
 
         <!-- nifi-registry.properties: provider properties -->
         
<nifi.registry.providers.configuration.file>./conf/providers.xml</nifi.registry.providers.configuration.file>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/pom.xml b/nifi-registry-data-model/pom.xml
index 3b6d4b1..6d548e4 100644
--- a/nifi-registry-data-model/pom.xml
+++ b/nifi-registry-data-model/pom.xml
@@ -34,5 +34,9 @@
             <groupId>javax.ws.rs</groupId>
             <artifactId>javax.ws.rs-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicy.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicy.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicy.java
new file mode 100644
index 0000000..dffaa7f
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicy.java
@@ -0,0 +1,72 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Access policy details, including the users and user groups to which the 
policy applies.
+ */
+@ApiModel("accessPolicy")
+public class AccessPolicy extends AccessPolicySummary {
+
+    private Set<Tenant> users;
+    private Set<Tenant> userGroups;
+
+    @ApiModelProperty(value = "The set of user IDs associated with this access 
policy.")
+    public Set<Tenant> getUsers() {
+        return users;
+    }
+
+    public void setUsers(Set<Tenant> users) {
+        this.users = users;
+    }
+
+    public void addUsers(Collection<? extends Tenant> users) {
+        if (users != null) {
+            if (this.users == null) {
+                this.users = new HashSet<>();
+            }
+            this.users.addAll(users);
+        }
+    }
+
+    @ApiModelProperty(value = "The set of user group IDs associated with this 
access policy.")
+    public Set<Tenant> getUserGroups() {
+        return userGroups;
+    }
+
+    public void setUserGroups(Set<Tenant> userGroups) {
+        this.userGroups = userGroups;
+    }
+
+    public void addUserGroups(Collection<? extends Tenant> userGroups) {
+        if (userGroups != null) {
+            if (this.userGroups == null) {
+                this.userGroups = new HashSet<>();
+            }
+            this.userGroups.addAll(userGroups);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java
new file mode 100644
index 0000000..e27fbd9
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java
@@ -0,0 +1,72 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Access policy summary of which actions ("read', "write") are allowable for 
a specified web resource.
+ */
+@ApiModel("accessPolicySummary")
+public class AccessPolicySummary {
+
+    private String identifier;
+    private String resource;
+    private String action;
+    private Boolean configurable;
+
+    @ApiModelProperty("The id of the policy. Set by server at creation time.")
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    @ApiModelProperty("The resource for this access policy.")
+    public String getResource() {
+        return resource;
+    }
+
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    @ApiModelProperty(
+            value = "The action associated with this access policy.",
+            allowableValues = "READ, WRITE"
+    )
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    @ApiModelProperty("Indicates if this access policy is configurable, based 
on which authorizer has been configured to manage it.")
+    public Boolean getConfigurable() {
+        return configurable;
+    }
+
+    public void setConfigurable(Boolean configurable) {
+        this.configurable = configurable;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java
new file mode 100644
index 0000000..2c66387
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java
@@ -0,0 +1,69 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+@ApiModel("accessStatus")
+public class AccessStatus {
+
+    public static enum Status {
+        UNKNOWN,
+        ACTIVE
+    }
+
+    private String identity;
+    private Status status;
+    private String message;
+
+    @ApiModelProperty(
+            value = "The user identity.",
+            readOnly = true
+    )
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+
+    @ApiModelProperty(
+            value = "The user access status.",
+            readOnly = true
+    )
+    public String getStatus() {
+        return status.toString();
+    }
+
+    public void setStatus(String status) {
+        this.status = Status.valueOf(status);
+    }
+
+    @ApiModelProperty(
+            value = "Additional details about the user access status.",
+            readOnly = true
+    )
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Resource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Resource.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Resource.java
new file mode 100644
index 0000000..a428958
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Resource.java
@@ -0,0 +1,56 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+@ApiModel("resource")
+public class Resource {
+
+    private String identifier;
+    private String name;
+
+    /**
+     * The name of the resource.
+     *
+     * @return The name of the resource
+     */
+    @ApiModelProperty("The name of the resource.")
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The identifier of the resource.
+     *
+     * @return The identifier of the resource
+     */
+    @ApiModelProperty("The identifier of the resource.")
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
new file mode 100644
index 0000000..662e3bf
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
@@ -0,0 +1,61 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * A tenant of this NiFi Registry
+ */
+@ApiModel("tenant")
+public class Tenant {
+
+    private String identifier;
+    private String identity;
+
+    public Tenant() {}
+
+    public Tenant(String identifier, String identity) {
+        this.identifier = identifier;
+        this.identity = identity;
+    }
+
+    /**
+     * @return tenant's unique identifier
+     */
+    @ApiModelProperty(value = "The computer-generated identifier of the 
tenant.")
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    /**
+     * @return tenant's identity
+     */
+    @ApiModelProperty(value = "The identity provider's identity of the 
tenant.")
+    public String getIdentity() {
+        return identity;
+    }
+
+    public void setIdentity(String identity) {
+        this.identity = identity;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/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
new file mode 100644
index 0000000..a15cbc7
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
@@ -0,0 +1,78 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+@ApiModel("user")
+public class User extends Tenant {
+
+    private Set<Tenant> userGroups;
+    private Set<AccessPolicySummary> accessPolicies;
+
+    public User(String identifier, String identity) {
+        super(identifier, identity);
+    }
+
+    @ApiModelProperty(
+            value = "The groups to which the user belongs. This field is read 
only.",
+            readOnly = true
+    )
+    public Set<Tenant> getUserGroups() {
+        return userGroups;
+    }
+
+    public void setUserGroups(Set<Tenant> userGroups) {
+        this.userGroups = userGroups;
+    }
+
+    public void addUserGroups(Collection<? extends Tenant> userGroups) {
+        if (userGroups != null) {
+            if (this.userGroups == null) {
+                this.userGroups = new HashSet<>();
+            }
+            this.userGroups.addAll(userGroups);
+        }
+    }
+
+    @ApiModelProperty(
+            value = "The access policies granted to this user. This field is 
read only",
+            readOnly = true
+    )
+    public Set<AccessPolicySummary> getAccessPolicies() {
+        return accessPolicies;
+    }
+
+    public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
+        this.accessPolicies = accessPolicies;
+    }
+
+    public void addAccessPolicies(Collection<? extends AccessPolicySummary> 
accessPolicies) {
+        if (accessPolicies != null) {
+            if (this.accessPolicies == null) {
+                this.accessPolicies = new HashSet<>();
+            }
+            this.accessPolicies.addAll(accessPolicies);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/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
new file mode 100644
index 0000000..d504e66
--- /dev/null
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
@@ -0,0 +1,83 @@
+/*
+ * 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.model.authorization;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A user group, used to apply a single set of authorization policies to a 
group of users.
+ */
+@ApiModel("userGroup")
+public class UserGroup extends Tenant {
+
+    private Set<Tenant> users;
+    private Set<AccessPolicySummary> accessPolicies;
+
+    public UserGroup(String identifier, String identity) {
+        super(identifier, identity);
+    }
+
+    /**
+     * @return The users that belong to this user group.
+     */
+    @ApiModelProperty(value = "The users that belong to this user group.")
+    public Set<Tenant> getUsers() {
+        return users;
+    }
+
+    public void setUsers(Set<Tenant> users) {
+        this.users = users;
+    }
+
+    public void addUsers(Collection<? extends Tenant> users) {
+        if (users != null) {
+            if (this.users == null) {
+                this.users = new HashSet<>();
+            }
+            this.users.addAll(users);
+        }
+    }
+
+    /**
+     * @return The access policies set for this user group
+     */
+    @ApiModelProperty(
+            value = "The access policies granted to this user group.",
+            readOnly = true
+    )
+    public Set<AccessPolicySummary> getAccessPolicies() {
+        return accessPolicies;
+    }
+
+    public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
+        this.accessPolicies = accessPolicies;
+    }
+
+    public void addAccessPolicies(Collection<? extends AccessPolicySummary> 
accessPolicies) {
+        if (accessPolicies != null) {
+            if (this.accessPolicies == null) {
+                this.accessPolicies = new HashSet<>();
+            }
+            this.accessPolicies.addAll(accessPolicies);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/pom.xml b/nifi-registry-framework/pom.xml
index 8b708d2..573f9b2 100644
--- a/nifi-registry-framework/pom.xml
+++ b/nifi-registry-framework/pom.xml
@@ -39,7 +39,7 @@
                 <artifactId>jaxb2-maven-plugin</artifactId>
                 <executions>
                     <execution>
-                        <id>current</id>
+                        <id>providers</id>
                         <goals>
                             <goal>xjc</goal>
                         </goals>
@@ -47,13 +47,45 @@
                             
<packageName>org.apache.nifi.registry.provider.generated</packageName>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>authorizers</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            
<packageName>org.apache.nifi.registry.authorization.generated</packageName>
+                            <clearOutputDir>false</clearOutputDir>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
                 <configuration>
-                    <excludes>**/provider/generated/*.java,</excludes>
+                    
<excludes>**/authorization/generated/*.java,**/provider/generated/*.java,</excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.gmavenplus</groupId>
+                <artifactId>gmavenplus-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>addTestSources</goal>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
                 </configuration>
             </plugin>
         </plugins>
@@ -81,11 +113,27 @@
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-security-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope> <!-- will be in lib sir -->
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-security-api-impl</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+            <version>${spring.boot.version}</version>
+        </dependency>
+        <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.hibernate</groupId>
+            <groupId>org.hibernate.validator</groupId>
             <artifactId>hibernate-validator</artifactId>
         </dependency>
         <dependency>
@@ -139,5 +187,23 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <version>1.0-groovy-2.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-all</artifactId>
+            <version>2.4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>2.2.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java
new file mode 100644
index 0000000..94a74bc
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java
@@ -0,0 +1,75 @@
+/*
+ * 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.nifi.registry.authorization.resource.Authorizable;
+
+public interface AuthorizableLookup {
+    /**
+     * Get the authorizable for retrieving resources.
+     *
+     * @return authorizable
+     */
+    Authorizable getResourcesAuthorizable();
+
+    /**
+     * Get the authorizable for all tenants.
+     *
+     * Get the {@link Authorizable} that represents the resource of users and 
user groups.
+     * @return authorizable
+     */
+    Authorizable getTenantsAuthorizable();
+
+    /**
+     * Get the authorizable for all access policies.
+     *
+     * @return authorizable
+     */
+    Authorizable getPoliciesAuthorizable();
+
+    /**
+     * Get the authorizable for all Buckets.
+     *
+     * @return authorizable
+     */
+    Authorizable getBucketsAuthorizable();
+
+    /**
+     * Get the authorizable for the Bucket with the bucket id.
+     *
+     * @param bucketIdentifier bucket id
+     * @return authorizable
+     */
+    Authorizable getBucketAuthorizable(String bucketIdentifier);
+
+    /**
+     * Get the authorizable for the policy of the specified resource.
+     *
+     * @param resource resource
+     * @return authorizable
+     */
+    Authorizable getAccessPolicyByResource(String resource);
+
+    /**
+     * Get the authorizable of the specified resource.
+     *
+     * @param resource resource
+     * @return authorizable
+     */
+    Authorizable getAuthorizableByResource(final String resource);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java
new file mode 100644
index 0000000..e6efb51
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+public interface AuthorizeAccess {
+    void authorize(AuthorizableLookup lookup);
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java
new file mode 100644
index 0000000..e6cf79f
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+public final class AuthorizerCapabilityDetection {
+
+    public static boolean isManagedAuthorizer(final Authorizer authorizer) {
+        return authorizer instanceof ManagedAuthorizer;
+    }
+
+    public static boolean isConfigurableAccessPolicyProvider(final Authorizer 
authorizer) {
+        if (!isManagedAuthorizer(authorizer)) {
+            return false;
+        }
+
+        final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) 
authorizer;
+        return managedAuthorizer.getAccessPolicyProvider() instanceof 
ConfigurableAccessPolicyProvider;
+    }
+
+    public static boolean isConfigurableUserGroupProvider(final Authorizer 
authorizer) {
+        if (!isManagedAuthorizer(authorizer)) {
+            return false;
+        }
+
+        final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) 
authorizer;
+        final AccessPolicyProvider accessPolicyProvider = 
managedAuthorizer.getAccessPolicyProvider();
+        return accessPolicyProvider.getUserGroupProvider() instanceof 
ConfigurableUserGroupProvider;
+    }
+
+    public static boolean isUserConfigurable(final Authorizer authorizer, 
final User user) {
+        if (!isConfigurableUserGroupProvider(authorizer)) {
+            return false;
+        }
+
+        final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) 
authorizer;
+        final ConfigurableUserGroupProvider configurableUserGroupProvider = 
(ConfigurableUserGroupProvider) 
managedAuthorizer.getAccessPolicyProvider().getUserGroupProvider();
+        return configurableUserGroupProvider.isConfigurable(user);
+    }
+
+    public static boolean isGroupConfigurable(final Authorizer authorizer, 
final Group group) {
+        if (!isConfigurableUserGroupProvider(authorizer)) {
+            return false;
+        }
+
+        final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) 
authorizer;
+        final ConfigurableUserGroupProvider configurableUserGroupProvider = 
(ConfigurableUserGroupProvider) 
managedAuthorizer.getAccessPolicyProvider().getUserGroupProvider();
+        return configurableUserGroupProvider.isConfigurable(group);
+    }
+
+    public static boolean isAccessPolicyConfigurable(final Authorizer 
authorizer, final AccessPolicy accessPolicy) {
+        if (!isConfigurableAccessPolicyProvider(authorizer)) {
+            return false;
+        }
+
+        final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) 
authorizer;
+        final ConfigurableAccessPolicyProvider 
configurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) 
managedAuthorizer.getAccessPolicyProvider();
+        return configurableAccessPolicyProvider.isConfigurable(accessPolicy);
+    }
+
+    private AuthorizerCapabilityDetection() {}
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java
new file mode 100644
index 0000000..9cace73
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+public interface AuthorizerFactory {
+
+    /**
+     * Initialize the factory.
+     *
+     * @throws AuthorizerFactoryException if an error occurs during 
initialization
+     */
+    void initialize() throws AuthorizerFactoryException;
+
+    /**
+     * @return the configured Authorizer
+     */
+    Authorizer getAuthorizer() throws AuthorizerFactoryException;
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java
new file mode 100644
index 0000000..45e0e24
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+public class AuthorizerFactoryException extends RuntimeException {
+
+    public AuthorizerFactoryException(String message) {
+        super(message);
+    }
+
+    public AuthorizerFactoryException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AuthorizerFactoryException(Throwable cause) {
+        super(cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java
new file mode 100644
index 0000000..9664558
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java
@@ -0,0 +1,197 @@
+/*
+ * 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.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 java.util.HashSet;
+import java.util.Set;
+
+public class CompositeConfigurableUserGroupProvider extends 
CompositeUserGroupProvider implements ConfigurableUserGroupProvider {
+
+    static final String PROP_CONFIGURABLE_USER_GROUP_PROVIDER = "Configurable 
User Group Provider";
+
+    private UserGroupProviderLookup userGroupProviderLookup;
+    private ConfigurableUserGroupProvider configurableUserGroupProvider;
+
+    public CompositeConfigurableUserGroupProvider() {
+        super(true);
+    }
+
+    @Override
+    public void initialize(UserGroupProviderInitializationContext 
initializationContext) throws AuthorizerCreationException {
+        userGroupProviderLookup = 
initializationContext.getUserGroupProviderLookup();
+
+        // initialize the CompositeUserGroupProvider
+        super.initialize(initializationContext);
+    }
+
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+        final PropertyValue configurableUserGroupProviderKey = 
configurationContext.getProperty(PROP_CONFIGURABLE_USER_GROUP_PROVIDER);
+        if (!configurableUserGroupProviderKey.isSet()) {
+            throw new AuthorizerCreationException("The Configurable User Group 
Provider must be set.");
+        }
+
+        final UserGroupProvider userGroupProvider = 
userGroupProviderLookup.getUserGroupProvider(configurableUserGroupProviderKey.getValue());
+
+        if (userGroupProvider == null) {
+            throw new AuthorizerCreationException(String.format("Unable to 
locate the Configurable User Group Provider: %s", 
configurableUserGroupProviderKey));
+        }
+
+        if (!(userGroupProvider instanceof ConfigurableUserGroupProvider)) {
+            throw new AuthorizerCreationException(String.format("The 
Configurable User Group Provider is not configurable: %s", 
configurableUserGroupProviderKey));
+        }
+
+        configurableUserGroupProvider = (ConfigurableUserGroupProvider) 
userGroupProvider;
+
+        // configure the CompositeUserGroupProvider
+        super.onConfigured(configurationContext);
+    }
+
+    @Override
+    public String getFingerprint() throws AuthorizationAccessException {
+        return configurableUserGroupProvider.getFingerprint();
+    }
+
+    @Override
+    public void inheritFingerprint(String fingerprint) throws 
AuthorizationAccessException {
+        configurableUserGroupProvider.inheritFingerprint(fingerprint);
+    }
+
+    @Override
+    public void checkInheritability(String proposedFingerprint) throws 
AuthorizationAccessException, UninheritableAuthorizationsException {
+        configurableUserGroupProvider.checkInheritability(proposedFingerprint);
+    }
+
+    @Override
+    public User addUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.addUser(user);
+    }
+
+    @Override
+    public boolean isConfigurable(User user) {
+        return configurableUserGroupProvider.isConfigurable(user);
+    }
+
+    @Override
+    public User updateUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.updateUser(user);
+    }
+
+    @Override
+    public User deleteUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteUser(user);
+    }
+
+    @Override
+    public User deleteUser(String userIdentifier) throws 
AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteUser(userIdentifier);
+    }
+
+    @Override
+    public Group addGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.addGroup(group);
+    }
+
+    @Override
+    public boolean isConfigurable(Group group) {
+        return configurableUserGroupProvider.isConfigurable(group);
+    }
+
+    @Override
+    public Group updateGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.updateGroup(group);
+    }
+
+    @Override
+    public Group deleteGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteGroup(group);
+    }
+
+    @Override
+    public Group deleteGroup(String groupIdentifier) throws 
AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteGroup(groupIdentifier);
+    }
+
+    @Override
+    public Set<User> getUsers() throws AuthorizationAccessException {
+        final Set<User> users = new 
HashSet<>(configurableUserGroupProvider.getUsers());
+        users.addAll(super.getUsers());
+        return users;
+    }
+
+    @Override
+    public User getUser(String identifier) throws AuthorizationAccessException 
{
+        User user = configurableUserGroupProvider.getUser(identifier);
+
+        if (user == null) {
+            user = super.getUser(identifier);
+        }
+
+        return user;
+    }
+
+    @Override
+    public User getUserByIdentity(String identity) throws 
AuthorizationAccessException {
+        User user = configurableUserGroupProvider.getUserByIdentity(identity);
+
+        if (user == null) {
+            user = super.getUserByIdentity(identity);
+        }
+
+        return user;
+    }
+
+    @Override
+    public Set<Group> getGroups() throws AuthorizationAccessException {
+        final Set<Group> groups = new 
HashSet<>(configurableUserGroupProvider.getGroups());
+        groups.addAll(super.getGroups());
+        return groups;
+    }
+
+    @Override
+    public Group getGroup(String identifier) throws 
AuthorizationAccessException {
+        Group group = configurableUserGroupProvider.getGroup(identifier);
+
+        if (group == null) {
+            group = super.getGroup(identifier);
+        }
+
+        return group;
+    }
+
+    @Override
+    public UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException {
+        UserAndGroups userAndGroups = 
configurableUserGroupProvider.getUserAndGroups(identity);
+
+        if (userAndGroups.getUser() == null) {
+            userAndGroups = super.getUserAndGroups(identity);
+        }
+
+        return userAndGroups;
+    }
+
+    @Override
+    public void preDestruction() throws AuthorizerDestructionException {
+        super.preDestruction();
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java
new file mode 100644
index 0000000..d2f8c4e
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java
@@ -0,0 +1,177 @@
+/*
+ * 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.AuthorizationAccessException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CompositeUserGroupProvider implements UserGroupProvider {
+
+    static final String PROP_USER_GROUP_PROVIDER_PREFIX = "User Group Provider 
";
+    static final Pattern USER_GROUP_PROVIDER_PATTERN = 
Pattern.compile(PROP_USER_GROUP_PROVIDER_PREFIX + "\\S+");
+
+    private final boolean allowEmptyProviderList;
+
+    private UserGroupProviderLookup userGroupProviderLookup;
+    private List<UserGroupProvider> userGroupProviders = new ArrayList<>(); // 
order matters
+
+    public CompositeUserGroupProvider() {
+        this(false);
+    }
+
+    public CompositeUserGroupProvider(boolean allowEmptyProviderList) {
+        this.allowEmptyProviderList = allowEmptyProviderList;
+    }
+
+    @Override
+    public void initialize(UserGroupProviderInitializationContext 
initializationContext) throws AuthorizerCreationException {
+        userGroupProviderLookup = 
initializationContext.getUserGroupProviderLookup();
+    }
+
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+        for (Map.Entry<String,String> entry : 
configurationContext.getProperties().entrySet()) {
+            Matcher matcher = 
USER_GROUP_PROVIDER_PATTERN.matcher(entry.getKey());
+            if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
+                final String userGroupProviderKey = entry.getValue();
+                final UserGroupProvider userGroupProvider = 
userGroupProviderLookup.getUserGroupProvider(userGroupProviderKey);
+
+                if (userGroupProvider == null) {
+                    throw new 
AuthorizerCreationException(String.format("Unable to locate the configured User 
Group Provider: %s", userGroupProviderKey));
+                }
+
+                userGroupProviders.add(userGroupProvider);
+            }
+        }
+
+        if (!allowEmptyProviderList && userGroupProviders.isEmpty()) {
+            throw new AuthorizerCreationException("At least one User Group 
Provider must be configured.");
+        }
+    }
+
+    @Override
+    public Set<User> getUsers() throws AuthorizationAccessException {
+        final Set<User> users = new HashSet<>();
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            users.addAll(userGroupProvider.getUsers());
+        }
+
+        return users;
+    }
+
+    @Override
+    public User getUser(String identifier) throws AuthorizationAccessException 
{
+        User user = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            user = userGroupProvider.getUser(identifier);
+
+            if (user != null) {
+                break;
+            }
+        }
+
+        return user;
+    }
+
+    @Override
+    public User getUserByIdentity(String identity) throws 
AuthorizationAccessException {
+        User user = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            user = userGroupProvider.getUserByIdentity(identity);
+
+            if (user != null) {
+                break;
+            }
+        }
+
+        return user;
+    }
+
+    @Override
+    public Set<Group> getGroups() throws AuthorizationAccessException {
+        final Set<Group> groups = new HashSet<>();
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            groups.addAll(userGroupProvider.getGroups());
+        }
+
+        return groups;
+    }
+
+    @Override
+    public Group getGroup(String identifier) throws 
AuthorizationAccessException {
+        Group group = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            group = userGroupProvider.getGroup(identifier);
+
+            if (group != null) {
+                break;
+            }
+        }
+
+        return group;
+    }
+
+    @Override
+    public UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException {
+        UserAndGroups userAndGroups = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            userAndGroups = userGroupProvider.getUserAndGroups(identity);
+
+            if (userAndGroups.getUser() != null) {
+                break;
+            }
+        }
+
+        if (userAndGroups == null) {
+            // per API, returning non null with null user/groups
+            return new UserAndGroups() {
+                @Override
+                public User getUser() {
+                    return null;
+                }
+
+                @Override
+                public Set<Group> getGroups() {
+                    return null;
+                }
+            };
+        } else {
+            // a delegated provider contained a matching user
+            return userAndGroups;
+        }
+    }
+
+    @Override
+    public void preDestruction() throws AuthorizerDestructionException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java
new file mode 100644
index 0000000..29339c7
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java
@@ -0,0 +1,218 @@
+/*
+ * 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.resource.Authorizable;
+import org.apache.nifi.registry.authorization.resource.ResourceFactory;
+import org.apache.nifi.registry.authorization.resource.ResourceType;
+import 
org.apache.nifi.registry.authorization.resource.AccessPolicyAuthorizable;
+import org.apache.nifi.registry.exception.ResourceNotFoundException;
+
+// TODO, make this spring-wired bean
+public class StandardAuthorizableLookup implements AuthorizableLookup {
+
+    private static final Authorizable TENANTS_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getTenantResource();
+        }
+    };
+
+    private static final Authorizable POLICIES_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getPoliciesResource();
+        }
+    };
+
+    private static final Authorizable RESOURCES_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getResourceResource();
+        }
+    };
+
+    private static final Authorizable BUCKETS_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getBucketsResource();
+        }
+    };
+
+    @Override
+    public Authorizable getResourcesAuthorizable() {
+        return RESOURCES_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getTenantsAuthorizable() {
+        return TENANTS_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getPoliciesAuthorizable() {
+        return POLICIES_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getBucketsAuthorizable() {
+        return BUCKETS_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getBucketAuthorizable(String bucketIdentifier) {
+        return new Authorizable() {
+
+            @Override
+            public Authorizable getParentAuthorizable() {
+                return getBucketsAuthorizable();
+            }
+
+            @Override
+            public Resource getResource() {
+                return ResourceFactory.getBucketResource(bucketIdentifier, 
null);
+            }
+        };
+    }
+
+    @Override
+    public Authorizable getAccessPolicyByResource(final String resource) {
+        try {
+            return new 
AccessPolicyAuthorizable(getAuthorizableByResource(resource));
+        } catch (final ResourceNotFoundException e) {
+            // the underlying component has been removed or resource is 
invalid... require /policies permissions
+            return POLICIES_AUTHORIZABLE;
+        }
+    }
+
+    @Override
+    public Authorizable getAuthorizableByResource(String resource) {
+        // parse the resource type
+        ResourceType resourceType = null;
+        for (ResourceType type : ResourceType.values()) {
+            if (resource.equals(type.getValue()) || 
resource.startsWith(type.getValue() + "/")) {
+                resourceType = type;
+            }
+        }
+
+        if (resourceType == null) {
+            throw new ResourceNotFoundException("Unrecognized resource: " + 
resource);
+        }
+
+        // if this is a policy resource, there should be another resource type
+        if (ResourceType.Policy.equals(resourceType)) {
+            final ResourceType primaryResourceType = resourceType;
+
+            // get the resource type
+            resource = StringUtils.substringAfter(resource, 
resourceType.getValue());
+
+            for (ResourceType type : ResourceType.values()) {
+                if (resource.equals(type.getValue()) || 
resource.startsWith(type.getValue() + "/")) {
+                    resourceType = type;
+                }
+            }
+
+            if (resourceType == null) {
+                throw new ResourceNotFoundException("Unrecognized resource: " 
+ resource);
+            }
+
+            return new AccessPolicyAuthorizable(getAccessPolicy(resourceType, 
resource));
+        } else {
+            return getAccessPolicy(resourceType, resource);
+        }
+    }
+
+    private Authorizable getAccessPolicy(final ResourceType resourceType, 
final String resource) {
+        final String slashComponentId = StringUtils.substringAfter(resource, 
resourceType.getValue());
+        if (slashComponentId.startsWith("/")) {
+            return getAccessPolicyByResource(resourceType, 
slashComponentId.substring(1));
+        } else {
+            return getAccessPolicyByResource(resourceType);
+        }
+    }
+
+    private Authorizable getAccessPolicyByResource(final ResourceType 
resourceType, final String childResourceId) {
+        Authorizable authorizable = null;
+        switch (resourceType) {
+            case Bucket:
+                authorizable = getBucketAuthorizable(childResourceId);
+        }
+
+        if (authorizable == null) {
+            throw new IllegalArgumentException("An unexpected type of resource 
in this policy " + resourceType.getValue());
+        }
+
+        return authorizable;
+    }
+
+    private Authorizable getAccessPolicyByResource(final ResourceType 
resourceType) {
+        Authorizable authorizable = null;
+        switch (resourceType) {
+
+            case Bucket:
+                authorizable = getBucketsAuthorizable();
+                break;
+            case Policy:
+                authorizable = getPoliciesAuthorizable();
+                break;
+            case Resource:
+                authorizable = new Authorizable() {
+                    @Override
+                    public Authorizable getParentAuthorizable() {
+                        return null;
+                    }
+
+                    @Override
+                    public Resource getResource() {
+                        return ResourceFactory.getResourceResource();
+                    }
+                };
+                break;
+            case Tenant:
+                authorizable = getTenantsAuthorizable();
+                break;
+        }
+
+        if (authorizable == null) {
+            throw new IllegalArgumentException("An unexpected type of resource 
in this policy " + resourceType.getValue());
+        }
+
+        return authorizable;
+    }
+
+}

Reply via email to