nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bbe...@apache.org
Subject [15/17] nifi-registry git commit: NIFIREG-33 Add LDAP and JWT auth support
Date Tue, 07 Nov 2017 18:50:31 GMT
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
deleted file mode 100644
index 4987a38..0000000
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
+++ /dev/null
@@ -1,52 +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.resource;
-
-public enum ResourceType {
-    Bucket("/buckets"),
-    Policy("/policies"),
-    Proxy("/proxy"),
-    Resource("/resources"),
-    Tenant("/tenants");
-
-    final String value;
-
-    private ResourceType(final String value) {
-        this.value = value;
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-    public static ResourceType valueOfValue(final String rawValue) {
-        ResourceType type = null;
-
-        for (final ResourceType rt : values()) {
-            if (rt.getValue().equals(rawValue)) {
-                type = rt;
-                break;
-            }
-        }
-
-        if (type == null) {
-            throw new IllegalArgumentException("Unknown resource type value " + rawValue);
-        }
-
-        return type;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
deleted file mode 100644
index 5f49241..0000000
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
+++ /dev/null
@@ -1,52 +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.user;
-
-import java.util.Set;
-
-/**
- * A representation of a NiFi user that has logged into the application
- */
-public interface NiFiUser {
-
-    /**
-     * @return the unique identity of this user
-     */
-    String getIdentity();
-
-    /**
-     * @return the groups that this user belongs to if this nifi is configured to load user groups, null otherwise.
-     */
-    Set<String> getGroups();
-
-    /**
-     * @return the next user in the proxied entities chain, or <code>null</code> if no more users exist in the chain.
-     */
-    NiFiUser getChain();
-
-    /**
-     * @return <code>true</code> if the user is the unauthenticated Anonymous user
-     */
-    boolean isAnonymous();
-
-    /**
-     * @return the address of the client that made the request which created this user
-     */
-    String getClientAddress();
-
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
deleted file mode 100644
index 7b0da47..0000000
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
+++ /dev/null
@@ -1,91 +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.user;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
-
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- * User details for a NiFi user.
- */
-public class NiFiUserDetails implements UserDetails {
-
-    private final NiFiUser user;
-
-    /**
-     * Creates a new NiFiUserDetails.
-     *
-     * @param user user
-     */
-    public NiFiUserDetails(NiFiUser user) {
-        this.user = user;
-    }
-
-    /**
-     * Get the user for this UserDetails.
-     *
-     * @return user
-     */
-    public NiFiUser getNiFiUser() {
-        return user;
-    }
-
-    /**
-     * Returns the authorities that this NiFi user has.
-     *
-     * @return authorities
-     */
-    @Override
-    public Collection<? extends GrantedAuthority> getAuthorities() {
-        return Collections.EMPTY_SET;
-    }
-
-    @Override
-    public String getPassword() {
-        return StringUtils.EMPTY;
-    }
-
-    @Override
-    public String getUsername() {
-        return user.getIdentity();
-    }
-
-    @Override
-    public boolean isAccountNonExpired() {
-        return true;
-    }
-
-    @Override
-    public boolean isAccountNonLocked() {
-        return true;
-    }
-
-    @Override
-    public boolean isCredentialsNonExpired() {
-        return true;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return true;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
deleted file mode 100644
index 2ad508a..0000000
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
+++ /dev/null
@@ -1,91 +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.user;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Utility methods for retrieving information about the current application user.
- *
- */
-public final class NiFiUserUtils {
-
-    /**
-     * Returns the current NiFiUser or null if the current user is not a NiFiUser.
-     *
-     * @return user
-     */
-    public static NiFiUser getNiFiUser() {
-        NiFiUser user = null;
-
-        // obtain the principal in the current authentication
-        final SecurityContext context = SecurityContextHolder.getContext();
-        final Authentication authentication = context.getAuthentication();
-        if (authentication != null) {
-            Object principal = authentication.getPrincipal();
-            if (principal instanceof NiFiUserDetails) {
-                user = ((NiFiUserDetails) principal).getNiFiUser();
-            }
-        }
-
-        return user;
-    }
-
-    public static String getNiFiUserIdentity() {
-        // get the nifi user to extract the username
-        NiFiUser user = NiFiUserUtils.getNiFiUser();
-        if (user == null) {
-            return "unknown";
-        } else {
-            return user.getIdentity();
-        }
-    }
-
-    /**
-     * Builds the proxy chain for the specified user.
-     *
-     * @param user The current user
-     * @return The proxy chain for that user in List form
-     */
-    public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
-        // calculate the dn chain
-        final List<String> proxyChain = new ArrayList<>();
-
-        // build the dn chain
-        NiFiUser chainedUser = user;
-        while (chainedUser != null) {
-            // add the entry for this user
-            if (chainedUser.isAnonymous()) {
-                // use an empty string to represent an anonymous user in the proxy entities chain
-                proxyChain.add(StringUtils.EMPTY);
-            } else {
-                proxyChain.add(chainedUser.getIdentity());
-            }
-
-            // go to the next user in the chain
-            chainedUser = chainedUser.getChain();
-        }
-
-        return proxyChain;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java
deleted file mode 100644
index 3a8a8bc..0000000
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java
+++ /dev/null
@@ -1,189 +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.user;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * An implementation of NiFiUser.
- */
-public class StandardNiFiUser implements NiFiUser {
-
-    public static final String ANONYMOUS_IDENTITY = "anonymous";
-    public static final StandardNiFiUser ANONYMOUS = new Builder().identity(ANONYMOUS_IDENTITY).anonymous(true).build();
-
-    private final String identity;
-    private final Set<String> groups;
-    private final NiFiUser chain;
-    private final String clientAddress;
-    private final boolean isAnonymous;
-
-    private StandardNiFiUser(final Builder builder) {
-        this.identity = builder.identity;
-        this.groups = builder.groups == null ? null : Collections.unmodifiableSet(builder.groups);
-        this.chain = builder.chain;
-        this.clientAddress = builder.clientAddress;
-        this.isAnonymous = builder.isAnonymous;
-    }
-
-    /**
-     * This static builder allows the chain and clientAddress to be populated without allowing calling code to provide a non-anonymous identity of the anonymous user.
-     *
-     * @param chain the proxied entities in {@see NiFiUser} form
-     * @param clientAddress the address the request originated from
-     * @return an anonymous user instance with the identity "anonymous"
-     */
-    public static StandardNiFiUser populateAnonymousUser(NiFiUser chain, String clientAddress) {
-        return new Builder().identity(ANONYMOUS_IDENTITY).chain(chain).clientAddress(clientAddress).anonymous(true).build();
-    }
-
-    @Override
-    public String getIdentity() {
-        return identity;
-    }
-
-    @Override
-    public Set<String> getGroups() {
-        return groups;
-    }
-
-    @Override
-    public NiFiUser getChain() {
-        return chain;
-    }
-
-    @Override
-    public boolean isAnonymous() {
-        return isAnonymous;
-    }
-
-    @Override
-    public String getClientAddress() {
-        return clientAddress;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (!(obj instanceof NiFiUser)) {
-            return false;
-        }
-
-        final NiFiUser other = (NiFiUser) obj;
-        return Objects.equals(this.identity, other.getIdentity());
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 7;
-        hash = 53 * hash + Objects.hashCode(this.identity);
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        final String formattedGroups;
-        if (groups == null) {
-            formattedGroups = "none";
-        } else {
-            formattedGroups = StringUtils.join(groups, ", ");
-        }
-
-        return String.format("identity[%s], groups[%s]", getIdentity(), formattedGroups);
-    }
-
-    /**
-     * Builder for a StandardNiFiUser
-     */
-    public static class Builder {
-
-        private String identity;
-        private Set<String> groups;
-        private NiFiUser chain;
-        private String clientAddress;
-        private boolean isAnonymous = false;
-
-        /**
-         * Sets the identity.
-         *
-         * @param identity the identity string for the user (i.e. "Andy" or "CN=alopresto, OU=Apache NiFi")
-         * @return the builder
-         */
-        public Builder identity(final String identity) {
-            this.identity = identity;
-            return this;
-        }
-
-        /**
-         * Sets the groups.
-         *
-         * @param groups the user groups
-         * @return the builder
-         */
-        public Builder groups(final Set<String> groups) {
-            this.groups = groups;
-            return this;
-        }
-
-        /**
-         * Sets the chain.
-         *
-         * @param chain the proxy chain that leads to this users
-         * @return the builder
-         */
-        public Builder chain(final NiFiUser chain) {
-            this.chain = chain;
-            return this;
-        }
-
-        /**
-         * Sets the client address.
-         *
-         * @param clientAddress the source address of the request
-         * @return the builder
-         */
-        public Builder clientAddress(final String clientAddress) {
-            this.clientAddress = clientAddress;
-            return this;
-        }
-
-        /**
-         * Sets whether this user is the canonical "anonymous" user
-         *
-         * @param isAnonymous true to represent the canonical "anonymous" user
-         * @return the builder
-         */
-        private Builder anonymous(final boolean isAnonymous) {
-            this.isAnonymous = isAnonymous;
-            return this;
-        }
-
-        /**
-         * @return builds a StandardNiFiUser from the current state of the builder
-         */
-        public StandardNiFiUser build() {
-            return new StandardNiFiUser(this);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java
new file mode 100644
index 0000000..eb55251
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java
@@ -0,0 +1,117 @@
+/*
+ * 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.db;
+
+import org.apache.nifi.registry.db.entity.KeyEntity;
+import org.apache.nifi.registry.db.repository.KeyRepository;
+import org.apache.nifi.registry.security.key.Key;
+import org.apache.nifi.registry.security.key.KeyService;
+import org.apache.nifi.registry.service.DataModelMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+@Service
+public class DatabaseKeyService implements KeyService {
+
+    private static final Logger logger = LoggerFactory.getLogger(DatabaseKeyService.class);
+
+    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+    private final Lock readLock = lock.readLock();
+    private final Lock writeLock = lock.writeLock();
+
+    private KeyRepository keyRepository;
+
+    @Autowired
+    public DatabaseKeyService(KeyRepository keyRepository) {
+        this.keyRepository = keyRepository;
+    }
+
+    @Override
+    public Key getKey(String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id cannot be null");
+        }
+
+        Key key = null;
+        readLock.lock();
+        try {
+            KeyEntity keyEntity = keyRepository.findOne(id);
+            if (keyEntity != null) {
+                key = DataModelMapper.map(keyEntity);
+            } else {
+                logger.debug("No signing key found with id='" + id + "'");
+            }
+        } finally {
+            readLock.unlock();
+        }
+        return key;
+    }
+
+    @Override
+    public Key getOrCreateKey(String identity) {
+        if (identity == null) {
+            throw new IllegalArgumentException("Identity cannot be null");
+        }
+
+        Key key;
+        writeLock.lock();
+        try {
+            final KeyEntity existingKeyEntity = keyRepository.findOneByTenantIdentity(identity);
+            if (existingKeyEntity == null) {
+                logger.debug("No key found with identity='" + identity + "'. Creating new key.");
+
+                final KeyEntity newKeyEntity = new KeyEntity();
+                newKeyEntity.setId(UUID.randomUUID().toString());
+                newKeyEntity.setTenantIdentity(identity);
+                newKeyEntity.setKeyValue(UUID.randomUUID().toString());
+
+                final KeyEntity savedKeyEntity = keyRepository.save(newKeyEntity);
+
+                key = DataModelMapper.map(savedKeyEntity);
+            } else {
+                key = DataModelMapper.map(existingKeyEntity);
+            }
+        } finally {
+            writeLock.unlock();
+        }
+        return key;
+    }
+
+    @Override
+    public void deleteKey(String identity) {
+        if (identity == null) {
+            throw new IllegalArgumentException("Identity cannot be null");
+        }
+
+        Key key;
+        writeLock.lock();
+        try {
+            logger.debug("Deleting key with identity='" + identity + "'.");
+            keyRepository.deleteByTenantIdentity(identity);
+        } finally {
+            writeLock.unlock();
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java
new file mode 100644
index 0000000..7ec7d22
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.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.db.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "SIGNING_KEY")
+public class KeyEntity {
+
+    @Id
+    private String id;
+
+    @Column(name = "TENANT_IDENTITY", unique = true, nullable = false)
+    private String tenantIdentity;
+
+    @Column(name = "KEY_VALUE", nullable = false)
+    private String keyValue;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getTenantIdentity() {
+        return tenantIdentity;
+    }
+
+    public void setTenantIdentity(String tenantIdentity) {
+        this.tenantIdentity = tenantIdentity;
+    }
+
+    public String getKeyValue() {
+        return keyValue;
+    }
+
+    public void setKeyValue(String keyValue) {
+        this.keyValue = keyValue;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java
new file mode 100644
index 0000000..1f31776
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java
@@ -0,0 +1,31 @@
+/*
+ * 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.db.repository;
+
+import org.apache.nifi.registry.db.entity.KeyEntity;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * Spring Data Repository for KeyEntity.
+ */
+public interface KeyRepository extends CrudRepository<KeyEntity, String> {
+
+    KeyEntity findOneByTenantIdentity(String identity);
+
+    void deleteByTenantIdentity(String identity);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java
index d3703a4..27e8b91 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java
@@ -17,9 +17,10 @@
 package org.apache.nifi.registry.extension;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.registry.authorization.AccessPolicyProvider;
-import org.apache.nifi.registry.authorization.Authorizer;
-import org.apache.nifi.registry.authorization.UserGroupProvider;
+import org.apache.nifi.registry.security.authentication.LoginIdentityProvider;
+import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.UserGroupProvider;
 import org.apache.nifi.registry.flow.FlowPersistenceProvider;
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
 import org.slf4j.Logger;
@@ -54,6 +55,7 @@ public class ExtensionManager {
         classes.add(UserGroupProvider.class);
         classes.add(AccessPolicyProvider.class);
         classes.add(Authorizer.class);
+        classes.add(LoginIdentityProvider.class);
         EXTENSION_CLASSES = Collections.unmodifiableList(classes);
     }
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java
new file mode 100644
index 0000000..734a983
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java
@@ -0,0 +1,839 @@
+/*
+ * 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.security.authorization;
+
+import org.apache.nifi.registry.security.authorization.AccessPolicy;
+import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
+import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext;
+import org.apache.nifi.registry.security.authorization.AuthorizationRequest;
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext;
+import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider;
+import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider;
+import org.apache.nifi.registry.security.authorization.Group;
+import org.apache.nifi.registry.security.authorization.ManagedAuthorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.User;
+import org.apache.nifi.registry.security.authorization.UserAndGroups;
+import org.apache.nifi.registry.security.authorization.UserGroupProvider;
+import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException;
+import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
+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.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An Authorizer that provides management of users, groups, and policies.
+ */
+public abstract class AbstractPolicyBasedAuthorizer implements ManagedAuthorizer {
+
+    static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+    static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
+
+    static final String USER_ELEMENT = "user";
+    static final String GROUP_USER_ELEMENT = "groupUser";
+    static final String GROUP_ELEMENT = "group";
+    static final String POLICY_ELEMENT = "policy";
+    static final String POLICY_USER_ELEMENT = "policyUser";
+    static final String POLICY_GROUP_ELEMENT = "policyGroup";
+    static final String IDENTIFIER_ATTR = "identifier";
+    static final String IDENTITY_ATTR = "identity";
+    static final String NAME_ATTR = "name";
+    static final String RESOURCE_ATTR = "resource";
+    static final String ACTIONS_ATTR = "actions";
+
+    @Override
+    public final void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+        doOnConfigured(configurationContext);
+    }
+
+    /**
+     * Allows sub-classes to take action when onConfigured is called.
+     *
+     * @param configurationContext the configuration context
+     * @throws AuthorizerCreationException if an error occurs during onConfigured process
+     */
+    protected abstract void doOnConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
+
+    @Override
+    public final AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
+        final UsersAndAccessPolicies usersAndAccessPolicies = getUsersAndAccessPolicies();
+        final String resourceIdentifier = request.getResource().getIdentifier();
+
+        final AccessPolicy policy = usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, request.getAction());
+        if (policy == null) {
+            return AuthorizationResult.resourceNotFound();
+        }
+
+        final User user = usersAndAccessPolicies.getUser(request.getIdentity());
+        if (user == null) {
+            return AuthorizationResult.denied(String.format("Unknown user with identity '%s'.", request.getIdentity()));
+        }
+
+        final Set<Group> userGroups = usersAndAccessPolicies.getGroups(user.getIdentity());
+        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.isEmpty() || policy.getGroups().isEmpty()) {
+            return false;
+        }
+
+        for (Group userGroup : userGroups) {
+            if (policy.getGroups().contains(userGroup.getIdentifier())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds a new group.
+     *
+     * @param group the Group to add
+     * @return the added Group
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if a group with the same name already exists
+     */
+    public final synchronized Group addGroup(Group group) throws AuthorizationAccessException {
+        return doAddGroup(group);
+    }
+
+    /**
+     * Adds a new group.
+     *
+     * @param group the Group to add
+     * @return the added Group
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Group doAddGroup(Group group) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves a Group by id.
+     *
+     * @param identifier the identifier of the Group to retrieve
+     * @return the Group with the given identifier, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Group getGroup(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * The group represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param group an updated group instance
+     * @return the updated group instance, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a group with the same name
+     */
+    public final synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
+        return doUpdateGroup(group);
+    }
+
+    /**
+     * The group represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param group an updated group instance
+     * @return the updated group instance, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Group doUpdateGroup(Group group) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given group.
+     *
+     * @param group the group to delete
+     * @return the deleted group, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Group deleteGroup(Group group) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the group with the given identifier.
+     *
+     * @param groupIdentifier the id of the group to delete
+     * @return the deleted group, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Group deleteGroup(String groupIdentifier) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves all groups.
+     *
+     * @return a list of groups
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Set<Group> getGroups() throws AuthorizationAccessException;
+
+
+    /**
+     * Adds the given user.
+     *
+     * @param user the user to add
+     * @return the user that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a user with the same identity
+     */
+    public final synchronized User addUser(User user) throws AuthorizationAccessException {
+        return doAddUser(user);
+    }
+
+    /**
+     * Adds the given user.
+     *
+     * @param user the user to add
+     * @return the user that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User doAddUser(User user) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identifier.
+     *
+     * @param identifier the id of the user to retrieve
+     * @return the user with the given id, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User getUser(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identity.
+     *
+     * @param identity the identity of the user to retrieve
+     * @return the user with the given identity, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User getUserByIdentity(String identity) throws AuthorizationAccessException;
+
+    /**
+     * The user represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param user an updated user instance
+     * @return the updated user instance, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a user with the same identity
+     */
+    public final synchronized User updateUser(final User user) throws AuthorizationAccessException {
+        return doUpdateUser(user);
+    }
+
+    /**
+     * The user represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param user an updated user instance
+     * @return the updated user instance, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User doUpdateUser(User user) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given user.
+     *
+     * @param user the user to delete
+     * @return the user that was deleted, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User deleteUser(User user) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the user with the given id.
+     *
+     * @param userIdentifier the identifier of the user to delete
+     * @return the user that was deleted, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract User deleteUser(String userIdentifier) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves all users.
+     *
+     * @return a list of users
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Set<User> getUsers() throws AuthorizationAccessException;
+
+    /**
+     * Adds the given policy ensuring that multiple policies can not be added for the same resource and action.
+     *
+     * @param accessPolicy the policy to add
+     * @return the policy that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public final synchronized AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+        return doAddAccessPolicy(accessPolicy);
+    }
+
+    /**
+     * Adds the given policy.
+     *
+     * @param accessPolicy the policy to add
+     * @return the policy that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    protected abstract AccessPolicy doAddAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the policy with the given identifier.
+     *
+     * @param identifier the id of the policy to retrieve
+     * @return the policy with the given id, or null if no matching policy exists
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * The policy represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param accessPolicy an updated policy
+     * @return the updated policy, or null if no matching policy was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given policy.
+     *
+     * @param policy the policy to delete
+     * @return the deleted policy, or null if no matching policy was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract AccessPolicy deleteAccessPolicy(AccessPolicy policy) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the policy with the given id.
+     *
+     * @param policyIdentifier the id of the policy to delete
+     * @return the deleted policy, or null if no matching policy was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract AccessPolicy deleteAccessPolicy(String policyIdentifier) throws AuthorizationAccessException;
+
+    /**
+     * Retrieves all access policies.
+     *
+     * @return a list of policies
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException;
+
+    /**
+     * Returns the UserAccessPolicies instance.
+     *
+     * @return the UserAccessPolicies instance
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    public abstract UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException;
+
+    /**
+     * Returns whether the proposed fingerprint is inheritable.
+     *
+     * @param proposedFingerprint the proposed fingerprint
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
+     */
+    @Override
+    public final void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+        try {
+            // ensure we understand the proposed fingerprint
+            parsePoliciesUsersAndGroups(proposedFingerprint);
+        } catch (final AuthorizationAccessException e) {
+            throw new UninheritableAuthorizationsException("Unable to parse proposed fingerprint: " + e);
+        }
+
+        final List<User> users = getSortedUsers();
+        final List<Group> groups = getSortedGroups();
+        final List<AccessPolicy> accessPolicies = getSortedAccessPolicies();
+
+        // ensure we're in a state to inherit
+        if (!users.isEmpty() || !groups.isEmpty() || !accessPolicies.isEmpty()) {
+            throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current Authorizations is not empty..");
+        }
+    }
+
+    /**
+     * Parses the fingerprint and adds any users, groups, and policies to the current Authorizer.
+     *
+     * @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
+     */
+    @Override
+    public final void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException {
+        if (fingerprint == null || fingerprint.trim().isEmpty()) {
+            return;
+        }
+
+        final PoliciesUsersAndGroups policiesUsersAndGroups = parsePoliciesUsersAndGroups(fingerprint);
+        policiesUsersAndGroups.getUsers().forEach(user -> addUser(user));
+        policiesUsersAndGroups.getGroups().forEach(group -> addGroup(group));
+        policiesUsersAndGroups.getAccessPolicies().forEach(policy -> addAccessPolicy(policy));
+    }
+
+    private PoliciesUsersAndGroups parsePoliciesUsersAndGroups(final String fingerprint) {
+        final List<AccessPolicy> accessPolicies = new ArrayList<>();
+        final List<User> users = new ArrayList<>();
+        final List<Group> groups = new ArrayList<>();
+
+        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();
+
+            // parse all the users and add them to the current authorizer
+            NodeList userNodes = rootElement.getElementsByTagName(USER_ELEMENT);
+            for (int i=0; i < userNodes.getLength(); i++) {
+                Node userNode = userNodes.item(i);
+                users.add(parseUser((Element) userNode));
+            }
+
+            // parse all the groups and add them to the current authorizer
+            NodeList groupNodes = rootElement.getElementsByTagName(GROUP_ELEMENT);
+            for (int i=0; i < groupNodes.getLength(); i++) {
+                Node groupNode = groupNodes.item(i);
+                groups.add(parseGroup((Element) groupNode));
+            }
+
+            // parse all the policies and add them to the current authorizer
+            NodeList policyNodes = rootElement.getElementsByTagName(POLICY_ELEMENT);
+            for (int i=0; i < policyNodes.getLength(); i++) {
+                Node policyNode = policyNodes.item(i);
+                accessPolicies.add(parsePolicy((Element) policyNode));
+            }
+        } catch (SAXException | ParserConfigurationException | IOException e) {
+            throw new AuthorizationAccessException("Unable to parse fingerprint", e);
+        }
+
+        return new PoliciesUsersAndGroups(accessPolicies, users, groups);
+    }
+
+    private User parseUser(final Element element) {
+        final User.Builder builder = new User.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .identity(element.getAttribute(IDENTITY_ATTR));
+
+        return builder.build();
+    }
+
+    private Group parseGroup(final Element element) {
+        final Group.Builder builder = new Group.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .name(element.getAttribute(NAME_ATTR));
+
+        NodeList groupUsers = element.getElementsByTagName(GROUP_USER_ELEMENT);
+        for (int i=0; i < groupUsers.getLength(); i++) {
+            Element groupUserNode = (Element) groupUsers.item(i);
+            builder.addUser(groupUserNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        return builder.build();
+    }
+
+    private AccessPolicy parsePolicy(final Element element) {
+        final AccessPolicy.Builder builder = new AccessPolicy.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .resource(element.getAttribute(RESOURCE_ATTR));
+
+        final String actions = element.getAttribute(ACTIONS_ATTR);
+        if (actions.equals(RequestAction.READ.name())) {
+            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);
+        }
+
+        NodeList policyUsers = element.getElementsByTagName(POLICY_USER_ELEMENT);
+        for (int i=0; i < policyUsers.getLength(); i++) {
+            Element policyUserNode = (Element) policyUsers.item(i);
+            builder.addUser(policyUserNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        NodeList policyGroups = element.getElementsByTagName(POLICY_GROUP_ELEMENT);
+        for (int i=0; i < policyGroups.getLength(); i++) {
+            Element policyGroupNode = (Element) policyGroups.item(i);
+            builder.addGroup(policyGroupNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        return builder.build();
+    }
+
+    @Override
+    public final AccessPolicyProvider getAccessPolicyProvider() {
+        return new ConfigurableAccessPolicyProvider() {
+            @Override
+            public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.getAccessPolicies();
+            }
+
+            @Override
+            public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.getAccessPolicy(identifier);
+            }
+
+            @Override
+            public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.addAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.updateAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.deleteAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy deleteAccessPolicy(String accessPolicyIdentifier) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.deleteAccessPolicy(accessPolicyIdentifier);
+            }
+
+            @Override
+            public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
+                final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
+                return usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, action);
+            }
+
+            @Override
+            public String getFingerprint() throws AuthorizationAccessException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public UserGroupProvider getUserGroupProvider() {
+                return new ConfigurableUserGroupProvider() {
+                    @Override
+                    public User addUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.addUser(user);
+                    }
+
+                    @Override
+                    public User updateUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.updateUser(user);
+                    }
+
+                    @Override
+                    public User deleteUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteUser(user);
+                    }
+
+                    @Override
+                    public User deleteUser(String userIdentifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteUser(userIdentifier);
+                    }
+
+                    @Override
+                    public Group addGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.addGroup(group);
+                    }
+
+                    @Override
+                    public Group updateGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.updateGroup(group);
+                    }
+
+                    @Override
+                    public Group deleteGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteGroup(group);
+                    }
+
+                    @Override
+                    public Group deleteGroup(String groupIdentifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteGroup(groupIdentifier);
+                    }
+
+                    @Override
+                    public Set<User> getUsers() throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUsers();
+                    }
+
+                    @Override
+                    public User getUser(String identifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUser(identifier);
+                    }
+
+                    @Override
+                    public User getUserByIdentity(String identity) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUserByIdentity(identity);
+                    }
+
+                    @Override
+                    public Set<Group> getGroups() throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getGroups();
+                    }
+
+                    @Override
+                    public Group getGroup(String identifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getGroup(identifier);
+                    }
+
+                    @Override
+                    public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
+                        final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
+                        final User user = usersAndAccessPolicies.getUser(identity);
+                        final Set<Group> groups = usersAndAccessPolicies.getGroups(identity);
+
+                        return new UserAndGroups() {
+                            @Override
+                            public User getUser() {
+                                return user;
+                            }
+
+                            @Override
+                            public Set<Group> getGroups() {
+                                return groups;
+                            }
+                        };
+                    }
+
+                    @Override
+                    public String getFingerprint() throws AuthorizationAccessException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
+                    }
+
+                    @Override
+                    public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+                    }
+
+                    @Override
+                    public void preDestruction() throws AuthorizerDestructionException {
+                    }
+                };
+            }
+
+            @Override
+            public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void preDestruction() throws AuthorizerDestructionException {
+            }
+        };
+    }
+
+    /**
+     * Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
+     * used for comparison to determine if two policy-based authorizers represent a compatible set of users,
+     * groups, and policies.
+     *
+     * @return the fingerprint for this Authorizer
+     */
+    @Override
+    public final String getFingerprint() throws AuthorizationAccessException {
+        final List<User> users = getSortedUsers();
+        final List<Group> groups = getSortedGroups();
+        final List<AccessPolicy> policies = getSortedAccessPolicies();
+
+        XMLStreamWriter writer = null;
+        final StringWriter out = new StringWriter();
+        try {
+            writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+            writer.writeStartDocument();
+            writer.writeStartElement("authorizations");
+
+            for (User user : users) {
+                writeUser(writer, user);
+            }
+            for (Group group : groups) {
+                writeGroup(writer, group);
+            }
+            for (AccessPolicy policy : policies) {
+                writePolicy(writer, policy);
+            }
+
+            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();
+    }
+
+    private void writeUser(final XMLStreamWriter writer, final User user) throws XMLStreamException {
+        writer.writeStartElement(USER_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, user.getIdentifier());
+        writer.writeAttribute(IDENTITY_ATTR, user.getIdentity());
+        writer.writeEndElement();
+    }
+
+    private void writeGroup(final XMLStreamWriter writer, final Group group) throws XMLStreamException {
+        List<String> users = new ArrayList<>(group.getUsers());
+        Collections.sort(users);
+
+        writer.writeStartElement(GROUP_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, group.getIdentifier());
+        writer.writeAttribute(NAME_ATTR, group.getName());
+
+        for (String user : users) {
+            writer.writeStartElement(GROUP_USER_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, user);
+            writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+    }
+
+    private void writePolicy(final XMLStreamWriter writer, final AccessPolicy policy) throws XMLStreamException {
+        // sort the users for the policy
+        List<String> policyUsers = new ArrayList<>(policy.getUsers());
+        Collections.sort(policyUsers);
+
+        // sort the groups for this policy
+        List<String> policyGroups = new ArrayList<>(policy.getGroups());
+        Collections.sort(policyGroups);
+
+        writer.writeStartElement(POLICY_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, policy.getIdentifier());
+        writer.writeAttribute(RESOURCE_ATTR, policy.getResource());
+        writer.writeAttribute(ACTIONS_ATTR, policy.getAction().name());
+
+        for (String policyUser : policyUsers) {
+            writer.writeStartElement(POLICY_USER_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, policyUser);
+            writer.writeEndElement();
+        }
+
+        for (String policyGroup : policyGroups) {
+            writer.writeStartElement(POLICY_GROUP_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, policyGroup);
+            writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+    }
+
+    private List<AccessPolicy> getSortedAccessPolicies() {
+        final List<AccessPolicy> policies = new ArrayList<>(getAccessPolicies());
+        Collections.sort(policies, Comparator.comparing(AccessPolicy::getIdentifier));
+        return policies;
+    }
+
+    private List<Group> getSortedGroups() {
+        final List<Group> groups = new ArrayList<>(getGroups());
+        Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
+        return groups;
+    }
+
+    private List<User> getSortedUsers() {
+        final List<User> users = new ArrayList<>(getUsers());
+        Collections.sort(users, Comparator.comparing(User::getIdentifier));
+        return users;
+    }
+
+    private static class PoliciesUsersAndGroups {
+        final List<AccessPolicy> accessPolicies;
+        final List<User> users;
+        final List<Group> groups;
+
+        public PoliciesUsersAndGroups(List<AccessPolicy> accessPolicies, List<User> users, List<Group> groups) {
+            this.accessPolicies = accessPolicies;
+            this.users = users;
+            this.groups = groups;
+        }
+
+        public List<AccessPolicy> getAccessPolicies() {
+            return accessPolicies;
+        }
+
+        public List<User> getUsers() {
+            return users;
+        }
+
+        public List<Group> getGroups() {
+            return groups;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
new file mode 100644
index 0000000..f5bbd4d
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.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.security.authorization;
+
+import org.apache.nifi.registry.security.authorization.resource.Authorizable;
+
+public interface AuthorizableLookup {
+
+    /**
+     * Get the authorizable for retrieving resources.
+     *
+     * @return authorizable
+     */
+    Authorizable getResourcesAuthorizable();
+
+    /**
+     * Get the authorizable for /proxy.
+     *
+     * @return authorizable
+     */
+    Authorizable getProxyAuthorizable();
+
+    /**
+     * 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/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
new file mode 100644
index 0000000..94dc3be
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/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.security.authorization;
+
+public interface AuthorizeAccess {
+    void authorize(AuthorizableLookup lookup);
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/90f36dd2/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java
new file mode 100644
index 0000000..5252c7f
--- /dev/null
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java
@@ -0,0 +1,84 @@
+/*
+ * 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.security.authorization;
+
+import org.apache.nifi.registry.security.authorization.AccessPolicy;
+import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider;
+import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider;
+import org.apache.nifi.registry.security.authorization.Group;
+import org.apache.nifi.registry.security.authorization.ManagedAuthorizer;
+import org.apache.nifi.registry.security.authorization.User;
+
+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() {}
+}


Mime
View raw message