This is an automated email from the ASF dual-hosted git repository.

bbende pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git

commit a126d0a6b6ae0d59c6b1deee3ef5252cb6d3e33b
Author: Tamas Palfy <[email protected]>
AuthorDate: Thu Jun 11 14:28:06 2020 +0200

    NIFI-7442 Added missing use cases (list users and user groups), made 
update-access-policy use case more in line with the NiFi side. Added some 
tests. Additional refactor, documentation revision.
    
    This closes #4329.
---
 nifi-docs/src/main/asciidoc/toolkit-guide.adoc     |   2 +-
 .../java/org/apache/nifi/toolkit/cli/CLIMain.java  |   2 +-
 .../client/JerseyExtendedNiFiRegistryClient.java   |  45 ++-
 .../cli/impl/client/NiFiRegistryClientFactory.java |   6 +-
 .../cli/impl/client/registry/PoliciesClient.java   |  35 +--
 .../cli/impl/client/registry/TenantsClient.java    |  62 ++--
 .../registry/impl/AbstractCRUDJerseyClient.java    |  91 ++++++
 .../client/registry/impl/JerseyPoliciesClient.java |  52 ++--
 .../client/registry/impl/JerseyTenantsClient.java  |  89 ++----
 .../toolkit/cli/impl/command/CommandOption.java    |   1 -
 .../command/registry/NiFiRegistryCommandGroup.java |  12 +-
 .../policy/CreateOrUpdateAccessPolicy.java         | 129 +++++++++
 .../GetAccessPolicy.java}                          |  54 +---
 .../{CreateUser.java => AbstractListTenants.java}  |  40 +--
 .../impl/command/registry/tenant/CreateUser.java   |   2 +-
 .../command/registry/tenant/CreateUserGroup.java   |  11 +-
 .../command/registry/tenant/ListUserGroups.java    |  47 ++++
 .../impl/command/registry/tenant/ListUsers.java    |  47 ++++
 .../impl/command/registry/tenant/TenantHelper.java |  74 +----
 .../impl/command/registry/tenant/UpdatePolicy.java | 112 --------
 .../impl/command/registry/tenant/UpdateUser.java   |  16 +-
 .../command/registry/tenant/UpdateUserGroup.java   |  24 +-
 .../impl/result/registry/AccessPolicyResult.java   |  59 ++++
 .../cli/impl/result/registry/UserGroupsResult.java |  82 ++++++
 .../cli/impl/result/registry/UsersResult.java      |  67 +++++
 .../impl/command/registry/RegistryManualIT.java    | 311 +++++++++++++++++++++
 .../command/registry/tenant/TestTenantHelper.java  | 153 ++++++++++
 27 files changed, 1170 insertions(+), 455 deletions(-)

diff --git a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc 
b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
index 44df219..c112fbb 100644
--- a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
@@ -110,7 +110,7 @@ The following are available commands:
  registry update-user
  registry create-user-group
  registry update-user-group
- registry create-policy
+ registry get-policy
  registry update-policy
  session keys
  session show
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
index 200d6ea..cc6b8f4 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java
@@ -136,7 +136,7 @@ public class CLIMain {
      *
      * @param args the args passed in from the command line
      */
-    private static int runSingleCommand(final String[] args) {
+    public static int runSingleCommand(final String[] args) {
         final Context context = createContext(System.out, false);
         final Map<String,Command> topLevelCommands = 
CommandFactory.createTopLevelCommands(context);
         final Map<String,CommandGroup> commandGroups = 
CommandFactory.createCommandGroups(context);
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/JerseyExtendedNiFiRegistryClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/JerseyExtendedNiFiRegistryClient.java
index edf0300..589ac97 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/JerseyExtendedNiFiRegistryClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/JerseyExtendedNiFiRegistryClient.java
@@ -69,17 +69,16 @@ public class JerseyExtendedNiFiRegistryClient implements 
ExtendedNiFiRegistryCli
     static final int DEFAULT_CONNECT_TIMEOUT = 10000;
     static final int DEFAULT_READ_TIMEOUT = 10000;
 
-    private final NiFiRegistryClient payload;
+    private final NiFiRegistryClient delegate;
     private final Client client;
     private final WebTarget baseTarget;
     private final TenantsClient tenantsClient;
     private final PoliciesClient policiesClient;
 
-    public JerseyExtendedNiFiRegistryClient(final NiFiRegistryClient payload, 
final NiFiRegistryClient.Builder builder) {
-        this.payload = payload;
+    public JerseyExtendedNiFiRegistryClient(final NiFiRegistryClient delegate, 
final NiFiRegistryClientConfig registryClientConfig) {
+        this.delegate = delegate;
 
         // Copied from JerseyNiFiRegistryClient!
-        final NiFiRegistryClientConfig registryClientConfig = 
builder.getConfig();
         if (registryClientConfig == null) {
             throw new IllegalArgumentException("NiFiRegistryClientConfig 
cannot be null");
         }
@@ -158,97 +157,97 @@ public class JerseyExtendedNiFiRegistryClient implements 
ExtendedNiFiRegistryCli
 
     @Override
     public BucketClient getBucketClient() {
-        return payload.getBucketClient();
+        return delegate.getBucketClient();
     }
 
     @Override
     public BucketClient getBucketClient(final String... proxiedEntity) {
-        return payload.getBucketClient(proxiedEntity);
+        return delegate.getBucketClient(proxiedEntity);
     }
 
     @Override
     public FlowClient getFlowClient() {
-        return payload.getFlowClient();
+        return delegate.getFlowClient();
     }
 
     @Override
     public FlowClient getFlowClient(final String... proxiedEntity) {
-        return payload.getFlowClient(proxiedEntity);
+        return delegate.getFlowClient(proxiedEntity);
     }
 
     @Override
     public FlowSnapshotClient getFlowSnapshotClient() {
-        return payload.getFlowSnapshotClient();
+        return delegate.getFlowSnapshotClient();
     }
 
     @Override
     public FlowSnapshotClient getFlowSnapshotClient(final String... 
proxiedEntity) {
-        return payload.getFlowSnapshotClient(proxiedEntity);
+        return delegate.getFlowSnapshotClient(proxiedEntity);
     }
 
     @Override
     public ItemsClient getItemsClient() {
-        return payload.getItemsClient();
+        return delegate.getItemsClient();
     }
 
     @Override
     public ItemsClient getItemsClient(final String... proxiedEntity) {
-        return payload.getItemsClient(proxiedEntity);
+        return delegate.getItemsClient(proxiedEntity);
     }
 
     @Override
     public UserClient getUserClient() {
-        return payload.getUserClient();
+        return delegate.getUserClient();
     }
 
     @Override
     public UserClient getUserClient(final String... proxiedEntity) {
-        return payload.getUserClient(proxiedEntity);
+        return delegate.getUserClient(proxiedEntity);
     }
 
     @Override
     public BundleClient getBundleClient() {
-        return payload.getBundleClient();
+        return delegate.getBundleClient();
     }
 
     @Override
     public BundleClient getBundleClient(final String... proxiedEntity) {
-        return payload.getBundleClient(proxiedEntity);
+        return delegate.getBundleClient(proxiedEntity);
     }
 
     @Override
     public BundleVersionClient getBundleVersionClient() {
-        return payload.getBundleVersionClient();
+        return delegate.getBundleVersionClient();
     }
 
     @Override
     public BundleVersionClient getBundleVersionClient(final String... 
proxiedEntity) {
-        return payload.getBundleVersionClient(proxiedEntity);
+        return delegate.getBundleVersionClient(proxiedEntity);
     }
 
     @Override
     public ExtensionRepoClient getExtensionRepoClient() {
-        return payload.getExtensionRepoClient();
+        return delegate.getExtensionRepoClient();
     }
 
     @Override
     public ExtensionRepoClient getExtensionRepoClient(final String... 
proxiedEntity) {
-        return payload.getExtensionRepoClient(proxiedEntity);
+        return delegate.getExtensionRepoClient(proxiedEntity);
     }
 
     @Override
     public ExtensionClient getExtensionClient() {
-        return payload.getExtensionClient();
+        return delegate.getExtensionClient();
     }
 
     @Override
     public ExtensionClient getExtensionClient(final String... proxiedEntity) {
-        return payload.getExtensionClient(proxiedEntity);
+        return delegate.getExtensionClient(proxiedEntity);
     }
 
     @Override
     public void close() throws IOException {
-        payload.close();
+        delegate.close();
 
         if (this.client != null) {
             try {
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiRegistryClientFactory.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiRegistryClientFactory.java
index cbdfd36..a2acbd0 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiRegistryClientFactory.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/NiFiRegistryClientFactory.java
@@ -99,9 +99,9 @@ public class NiFiRegistryClientFactory implements 
ClientFactory<NiFiRegistryClie
             }
         }
 
-        final NiFiRegistryClientConfig builder = clientConfigBuilder.build();
-        final NiFiRegistryClient client = new 
JerseyNiFiRegistryClient.Builder().config(builder).build();
-        final ExtendedNiFiRegistryClient extendedClient = new 
JerseyExtendedNiFiRegistryClient(client, new 
JerseyNiFiRegistryClient.Builder().config(builder));
+        final NiFiRegistryClientConfig clientConfig = 
clientConfigBuilder.build();
+        final NiFiRegistryClient client = new 
JerseyNiFiRegistryClient.Builder().config(clientConfig).build();
+        final ExtendedNiFiRegistryClient extendedClient = new 
JerseyExtendedNiFiRegistryClient(client, clientConfig);
 
         // if a proxied entity was specified then return a wrapped client, 
otherwise return the regular client
         if (!StringUtils.isBlank(proxiedEntity)) {
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/PoliciesClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/PoliciesClient.java
index be481a8..fe614f5 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/PoliciesClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/PoliciesClient.java
@@ -22,43 +22,44 @@ import 
org.apache.nifi.registry.client.NiFiRegistryException;
 import java.io.IOException;
 
 /**
- * Provides API for the services might be called from registry related to 
access policies.
+ * Provides API for access policies related services available in NiFi 
Registry.
  */
 public interface PoliciesClient {
 
     /**
-     * Returns with a given access policy.
+     * Returns a given access policy.
      *
-     * @param id The identifier of the access policy.
+     * @param resource The action allowed by the access policy.
+     * @param action The resource managed by the access policy.
      *
      * @return The access policy.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
-    AccessPolicy getPolicy(String id) throws NiFiRegistryException, 
IOException;
+    AccessPolicy getAccessPolicy(String action, String resource) throws 
NiFiRegistryException, IOException;
 
     /**
-     * Creates a new access policy within the registry.
+     * Creates a new access policy.
      *
-     * @param policy The attributes of the access policy. Note: identifier 
will be ignored and generated.
+     * @param policy The access policy to be created. Note: identifier will be 
ignored and assigned by NiFi Registry.
      *
-     * @return The access policy after store, containing it's identifier.
+     * @return The created access with an assigned identifier.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
-    AccessPolicy createPolicy(AccessPolicy policy) throws 
NiFiRegistryException, IOException;
+    AccessPolicy createAccessPolicy(AccessPolicy policy) throws 
NiFiRegistryException, IOException;
 
     /**
      * Updates an existing access policy.
      *
-     * @param policy The updated attributes of the access policy.
+     * @param policy The access policy with new attributes.
      *
-     * @return The stored access policy.
+     * @return The updated access policy.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
-    AccessPolicy updatePolicy(AccessPolicy policy) throws 
NiFiRegistryException, IOException;
+    AccessPolicy updateAccessPolicy(AccessPolicy policy) throws 
NiFiRegistryException, IOException;
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/TenantsClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/TenantsClient.java
index 5ad56f8..ab4c17b 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/TenantsClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/TenantsClient.java
@@ -24,41 +24,41 @@ import java.io.IOException;
 import java.util.List;
 
 /**
- * Provides API for the services might be called from registry related to 
tenants.
+ * Provides API for tenant (user/group) related services available in NiFi 
Registry.
  */
 public interface TenantsClient {
 
     /**
-     * Returns all the users.
+     * Returns all users.
      *
-     * @return The list of users in the registry.
+     * @return The list of users.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     List<User> getUsers() throws NiFiRegistryException, IOException;
 
     /**
-     * Returns a given user based on id.
+     * Returns a user with a given identifier.
      *
      * @param id Identifier of the user.
      *
      * @return The user.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     User getUser(String id) throws NiFiRegistryException, IOException;
 
     /**
-     * Creates a new user in the registry.
+     * Creates a new user in NiFi Registry.
      *
-     * @param user The new user. Note: identifier will be ignored and 
generated.
+     * @param user The new user. Note: identifier will be ignored and assigned 
be NiFi Registry.
      *
-     * @return The user after store, containing it's identifier.
+     * @return The created user with an assigned identifier.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     User createUser(User user) throws NiFiRegistryException, IOException;
 
@@ -67,56 +67,56 @@ public interface TenantsClient {
      *
      * @param user The user with the new attributes.
      *
-     * @return The user after store.
+     * @return The updated user.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     User updateUser(User user) throws NiFiRegistryException, IOException;
 
     /**
-     * Returns all the user groups.
+     * Returns all user groups.
      *
      * @return The list of user groups.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     List<UserGroup> getUserGroups() throws NiFiRegistryException, IOException;
 
     /**
-     * Returns the given user group based on identifier.
+     * Returns a user group with a given identifier.
      *
-     * @param id The user group's identifier.
+     * @param id Identifier of the user group.
      *
      * @return The user group.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     UserGroup getUserGroup(String id) throws NiFiRegistryException, 
IOException;
 
     /**
-     * Creates a new user group in the registry.
+     * Creates a new user group.
      *
-     * @param group The user group to store. Note: identifier will be ignored 
and generated.
+     * @param group The user group to be created. Note: identifier will be 
ignored and assigned by NiFi Registry.
      *
-     * @return The stored user group, containing id.
+     * @return The created user group with an assigned identifier.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.@throws IOException
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     UserGroup createUserGroup(UserGroup group) throws NiFiRegistryException, 
IOException;
 
     /**
      * Updates an existing user group.
      *
-     * @param group The user group with the new attributes.
+     * @param group The user group with new attributes.
      *
      * @return The user group after store.
      *
-     * @throws NiFiRegistryException Thrown in case os unsuccessful execution.
-     * @throws IOException Thrown when there is an issue with communicating 
the registry.
+     * @throws NiFiRegistryException Thrown in case of unsuccessful execution.
+     * @throws IOException Thrown when there is an issue while communicating 
with NiFi Registry.
      */
     UserGroup updateUserGroup(UserGroup group) throws NiFiRegistryException, 
IOException;
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/AbstractCRUDJerseyClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/AbstractCRUDJerseyClient.java
new file mode 100644
index 0000000..4aef09b
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/AbstractCRUDJerseyClient.java
@@ -0,0 +1,91 @@
+/*
+ * 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.toolkit.cli.impl.client.registry.impl;
+
+import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.registry.client.impl.AbstractJerseyClient;
+import org.apache.nifi.util.StringUtils;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.util.Map;
+
+public class AbstractCRUDJerseyClient extends AbstractJerseyClient {
+    protected final WebTarget baseTarget;
+
+    public AbstractCRUDJerseyClient(final WebTarget baseTarget, final 
Map<String, String> headers) {
+        super(headers);
+        this.baseTarget = baseTarget;
+    }
+
+    protected <T> T get(
+        String id,
+        Class<T> entityType,
+        String entityTypeName,
+        String entityPath
+    ) throws NiFiRegistryException, IOException {
+        if (StringUtils.isBlank(id)) {
+            throw new IllegalArgumentException(entityTypeName + " id cannot be 
blank");
+        }
+
+        return executeAction("Error retrieving " + 
entityTypeName.toLowerCase(), () -> {
+            final WebTarget target = baseTarget.path(entityPath).path(id);
+            return getRequestBuilder(target).get(entityType);
+        });
+    }
+
+    protected <T> T create(
+        T entity,
+        Class<T> entityType,
+        String entityTypeName,
+        String entityPath
+    ) throws NiFiRegistryException, IOException {
+        if (entity == null) {
+            throw new IllegalArgumentException(entityTypeName + " cannot be 
null");
+        }
+
+        return executeAction("Error creating " + entityTypeName.toLowerCase(), 
() -> {
+            final WebTarget target = baseTarget.path(entityPath);
+
+            return getRequestBuilder(target).post(
+                Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), 
entityType
+            );
+        });
+    }
+
+    protected <T> T update(
+        T entity,
+        String id,
+        Class<T> entityType,
+        String entityTypeName,
+        String entityPath
+    ) throws NiFiRegistryException, IOException {
+        if (entity == null) {
+            throw new IllegalArgumentException(entityTypeName + " cannot be 
null");
+        }
+
+        return executeAction("Error updating " + entityTypeName.toLowerCase(), 
() -> {
+            final WebTarget target = baseTarget.path(entityPath).path(id);
+
+            return getRequestBuilder(target).put(
+                Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), 
entityType
+            );
+        });
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyPoliciesClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyPoliciesClient.java
index ee7a806..282234a 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyPoliciesClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyPoliciesClient.java
@@ -16,67 +16,47 @@
  */
 package org.apache.nifi.toolkit.cli.impl.client.registry.impl;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.authorization.AccessPolicy;
 import org.apache.nifi.registry.client.NiFiRegistryException;
-import org.apache.nifi.registry.client.impl.AbstractJerseyClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.PoliciesClient;
-import org.apache.nifi.util.StringUtils;
 
-import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Map;
 
-public class JerseyPoliciesClient extends AbstractJerseyClient implements 
PoliciesClient {
-    private final WebTarget policiesTarget;
-
-    public JerseyPoliciesClient(final WebTarget baseTarget, final Map<String, 
String> headers) {
-        super(headers);
-        this.policiesTarget = baseTarget.path("/policies");
-    }
+public class JerseyPoliciesClient extends AbstractCRUDJerseyClient implements 
PoliciesClient {
+    public static final String ACCESS_POLICY = "Access policy";
+    public static final String POLICIES_PATH = "policies";
 
     public JerseyPoliciesClient(final WebTarget baseTarget) {
         this(baseTarget, Collections.emptyMap());
     }
 
+    public JerseyPoliciesClient(final WebTarget baseTarget, final Map<String, 
String> headers) {
+        super(baseTarget, headers);
+    }
+
     @Override
-    public AccessPolicy getPolicy(final String id) throws 
NiFiRegistryException, IOException {
-        if (StringUtils.isBlank(id)) {
-            throw new IllegalArgumentException("Access policy id cannot be 
null");
+    public AccessPolicy getAccessPolicy(String action, String resource) throws 
NiFiRegistryException, IOException {
+        if (StringUtils.isBlank(resource) || StringUtils.isBlank(action)) {
+            throw new IllegalArgumentException("Resource and action cannot be 
null");
         }
 
         return executeAction("Error retrieving access policy", () -> {
-            final WebTarget target = 
policiesTarget.path("{id}").resolveTemplate("id", id);
+            final WebTarget target = 
baseTarget.path(POLICIES_PATH).path(action).path(resource);
             return getRequestBuilder(target).get(AccessPolicy.class);
         });
     }
 
     @Override
-    public AccessPolicy createPolicy(final AccessPolicy policy) throws 
NiFiRegistryException, IOException {
-        if (policy == null) {
-            throw new IllegalArgumentException("Access policy cannot be null");
-        }
-
-        return executeAction("Error creating access policy", () -> {
-            return getRequestBuilder(policiesTarget).post(
-                    Entity.entity(policy, MediaType.APPLICATION_JSON_TYPE), 
AccessPolicy.class
-            );
-        });
+    public AccessPolicy createAccessPolicy(final AccessPolicy policy) throws 
NiFiRegistryException, IOException {
+        return create(policy, AccessPolicy.class, ACCESS_POLICY, 
POLICIES_PATH);
     }
 
     @Override
-    public AccessPolicy updatePolicy(final AccessPolicy policy) throws 
NiFiRegistryException, IOException {
-        if (policy == null) {
-            throw new IllegalArgumentException("Access policy cannot be null");
-        }
-
-        return executeAction("Error creating access policy", () -> {
-            final WebTarget target = 
policiesTarget.path("{id}").resolveTemplate("id", policy.getIdentifier());
-            return getRequestBuilder(target).put(
-                    Entity.entity(policy, MediaType.APPLICATION_JSON_TYPE), 
AccessPolicy.class
-            );
-        });
+    public AccessPolicy updateAccessPolicy(final AccessPolicy policy) throws 
NiFiRegistryException, IOException {
+        return update(policy, policy.getIdentifier(), AccessPolicy.class, 
ACCESS_POLICY, POLICIES_PATH);
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyTenantsClient.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyTenantsClient.java
index a135702..d59d8f8 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyTenantsClient.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/registry/impl/JerseyTenantsClient.java
@@ -19,128 +19,73 @@ package 
org.apache.nifi.toolkit.cli.impl.client.registry.impl;
 import org.apache.nifi.registry.authorization.User;
 import org.apache.nifi.registry.authorization.UserGroup;
 import org.apache.nifi.registry.client.NiFiRegistryException;
-import org.apache.nifi.registry.client.impl.AbstractJerseyClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
-import org.apache.nifi.util.StringUtils;
 
-import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
-public class JerseyTenantsClient extends AbstractJerseyClient implements 
TenantsClient {
-    private final WebTarget tenantsTarget;
+public class JerseyTenantsClient extends AbstractCRUDJerseyClient implements 
TenantsClient {
+    public static final String USER = "User";
+    public static final String USERS_PATH = "users";
 
-    public JerseyTenantsClient(final WebTarget baseTarget, final Map<String, 
String> headers) {
-        super(headers);
-        this.tenantsTarget = baseTarget.path("/tenants");
-    }
+    public static final String USER_GROUP = "User group";
+    public static final String USER_GROUPS_PATH = "user-groups";
 
     public JerseyTenantsClient(final WebTarget baseTarget) {
         this(baseTarget, Collections.emptyMap());
     }
 
+    public JerseyTenantsClient(final WebTarget baseTarget, final Map<String, 
String> headers) {
+        super(baseTarget.path("/tenants"), headers);
+    }
+
     @Override
     public List<User> getUsers() throws NiFiRegistryException, IOException {
         return executeAction("Error retrieving users", () -> {
-            final WebTarget target = tenantsTarget.path("users");
+            final WebTarget target = baseTarget.path(USERS_PATH);
             return Arrays.asList(getRequestBuilder(target).get(User[].class));
         });
     }
 
     @Override
     public User getUser(final String id) throws NiFiRegistryException, 
IOException {
-        if (StringUtils.isBlank(id)) {
-            throw new IllegalArgumentException("User id cannot be null");
-        }
-
-        return executeAction("Error retrieving user", () -> {
-            final WebTarget target = 
tenantsTarget.path("users/{id}").resolveTemplate("id", id);
-            return getRequestBuilder(target).get(User.class);
-        });
+        return get(id, User.class, USER, USERS_PATH);
     }
 
     @Override
     public User createUser(final User user) throws NiFiRegistryException, 
IOException {
-        if (user == null) {
-            throw new IllegalArgumentException("User cannot be null");
-        }
-
-        return executeAction("Error creating user", () -> {
-            final WebTarget target = tenantsTarget.path("users");
-
-            return getRequestBuilder(target).post(
-                Entity.entity(user, MediaType.APPLICATION_JSON_TYPE), 
User.class
-            );
-        });
+        return create(user, User.class, USER, USERS_PATH);
     }
 
     @Override
     public User updateUser(final User user) throws NiFiRegistryException, 
IOException {
-        if (user == null) {
-            throw new IllegalArgumentException("User cannot be null");
-        }
-
-        return executeAction("Error updating user", () -> {
-            final WebTarget target = 
tenantsTarget.path("users/{id}").resolveTemplate("id", user.getIdentifier());
-
-            return getRequestBuilder(target).put(
-                    Entity.entity(user, MediaType.APPLICATION_JSON_TYPE), 
User.class
-            );
-        });
+        return update(user, user.getIdentifier(), User.class, USER, 
USERS_PATH);
     }
 
     @Override
     public List<UserGroup> getUserGroups() throws NiFiRegistryException, 
IOException {
         return executeAction("Error retrieving users", () -> {
-            final WebTarget target = tenantsTarget.path("user-groups");
+            final WebTarget target = baseTarget.path(USER_GROUPS_PATH);
             return 
Arrays.asList(getRequestBuilder(target).get(UserGroup[].class));
         });
     }
 
     @Override
     public UserGroup getUserGroup(final String id) throws 
NiFiRegistryException, IOException {
-        if (StringUtils.isBlank(id)) {
-            throw new IllegalArgumentException("User group id cannot be null");
-        }
-
-        return executeAction("Error retrieving user group", () -> {
-            final WebTarget target = 
tenantsTarget.path("user-groups/{id}").resolveTemplate("id", id);
-            return getRequestBuilder(target).get(UserGroup.class);
-        });
+        return get(id, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
     }
 
     @Override
     public UserGroup createUserGroup(final UserGroup group) throws 
NiFiRegistryException, IOException {
-        if (group == null) {
-            throw new IllegalArgumentException("User group cannot be null");
-        }
-
-        return executeAction("Error creating group", () -> {
-            final WebTarget target = tenantsTarget.path("user-groups");
-
-            return getRequestBuilder(target).post(
-                    Entity.entity(group, MediaType.APPLICATION_JSON_TYPE), 
UserGroup.class
-            );
-        });
+        return create(group, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
     }
 
     @Override
     public UserGroup updateUserGroup(final UserGroup group) throws 
NiFiRegistryException, IOException {
-        if (group == null) {
-            throw new IllegalArgumentException("User group cannot be null");
-        }
-
-        return executeAction("Error creating group", () -> {
-            final WebTarget target = 
tenantsTarget.path("user-groups/{id}").resolveTemplate("id", 
group.getIdentifier());
-
-            return getRequestBuilder(target).put(
-                    Entity.entity(group, MediaType.APPLICATION_JSON_TYPE), 
UserGroup.class
-            );
-        });
+        return update(group, group.getIdentifier(), UserGroup.class, 
USER_GROUP, USER_GROUPS_PATH);
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
index eb5b28d..2af6dbf 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
@@ -102,7 +102,6 @@ public enum CommandOption {
     GROUP_ID_LIST("gil", "groupIdList", "The comma-separated user group id 
list", true),
 
     // NiFi - Access Policies
-    POLICY_ID("pi", "accessPolicyIdentifier", "The identifier of an access 
policy", true),
     POLICY_RESOURCE("por", "accessPolicyResource", "The resource of an access 
policy", true),
     POLICY_ACTION("poa", "accessPolicyAction", "The action of an access policy 
(read or write)", true),
     OVERWRITE_POLICY("owp", "overwritePolicy", "Overwrite the user list and 
group list for the access policy", false),
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java
index 8b9c93f..5580c16 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java
@@ -39,10 +39,12 @@ import 
org.apache.nifi.toolkit.cli.impl.command.registry.flow.ListFlowVersions;
 import org.apache.nifi.toolkit.cli.impl.command.registry.flow.ListFlows;
 import org.apache.nifi.toolkit.cli.impl.command.registry.flow.SyncFlowVersions;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.flow.TransferFlowVersion;
-import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.CreatePolicy;
+import 
org.apache.nifi.toolkit.cli.impl.command.registry.policy.GetAccessPolicy;
 import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.CreateUser;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.tenant.CreateUserGroup;
-import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.UpdatePolicy;
+import 
org.apache.nifi.toolkit.cli.impl.command.registry.policy.CreateOrUpdateAccessPolicy;
+import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.ListUserGroups;
+import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.ListUsers;
 import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.UpdateUser;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.tenant.UpdateUserGroup;
 import org.apache.nifi.toolkit.cli.impl.command.registry.user.CurrentUser;
@@ -86,12 +88,14 @@ public class NiFiRegistryCommandGroup extends 
AbstractCommandGroup {
         commandList.add(new GetBundleChecksum());
         commandList.add(new ListExtensionTags());
         commandList.add(new ListExtensions());
+        commandList.add(new ListUsers());
         commandList.add(new CreateUser());
         commandList.add(new UpdateUser());
+        commandList.add(new ListUserGroups());
         commandList.add(new CreateUserGroup());
         commandList.add(new UpdateUserGroup());
-        commandList.add(new CreatePolicy());
-        commandList.add(new UpdatePolicy());
+        commandList.add(new GetAccessPolicy());
+        commandList.add(new CreateOrUpdateAccessPolicy());
         return new ArrayList<>(commandList);
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/CreateOrUpdateAccessPolicy.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/CreateOrUpdateAccessPolicy.java
new file mode 100644
index 0000000..9a4f977
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/CreateOrUpdateAccessPolicy.java
@@ -0,0 +1,129 @@
+/*
+ * 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.toolkit.cli.impl.command.registry.policy;
+
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.authorization.AccessPolicy;
+import org.apache.nifi.registry.authorization.Tenant;
+import org.apache.nifi.registry.client.NiFiRegistryClient;
+import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
+import org.apache.nifi.toolkit.cli.impl.client.registry.PoliciesClient;
+import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
+import org.apache.nifi.toolkit.cli.impl.command.registry.tenant.TenantHelper;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Command for creating a new or updating an existing policy.
+ */
+public class CreateOrUpdateAccessPolicy extends 
AbstractNiFiRegistryCommand<VoidResult> {
+
+    public CreateOrUpdateAccessPolicy() {
+        super("update-policy", VoidResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Updates the access policy for the given resource and action, 
or creates the policy " +
+            "if it doesn't exist. In stand-alone mode this command will not 
produce all of " +
+            "the output seen in interactive mode unless the --verbose argument 
is specified.";
+    }
+
+    @Override
+    protected void doInitialize(final Context context) {
+        // Required
+        addOption(CommandOption.POLICY_RESOURCE.createOption());
+        addOption(CommandOption.POLICY_ACTION.createOption());
+
+        // Optional
+        addOption(CommandOption.USER_NAME_LIST.createOption());
+        addOption(CommandOption.USER_ID_LIST.createOption());
+        addOption(CommandOption.GROUP_NAME_LIST.createOption());
+        addOption(CommandOption.GROUP_ID_LIST.createOption());
+        addOption(CommandOption.OVERWRITE_POLICY.createOption());
+    }
+
+    @Override
+    public VoidResult doExecute(final NiFiRegistryClient client, final 
Properties properties) throws IOException, NiFiRegistryException, 
ParseException {
+        if (!(client instanceof ExtendedNiFiRegistryClient)) {
+            throw new IllegalArgumentException("This command needs extended 
registry client!");
+        }
+        final ExtendedNiFiRegistryClient extendedClient = 
(ExtendedNiFiRegistryClient) client;
+
+        final PoliciesClient policiesClient = 
extendedClient.getPoliciesClient();
+        final TenantsClient tenantsClient = extendedClient.getTenantsClient();
+
+        final String action = getRequiredArg(properties, 
CommandOption.POLICY_ACTION);
+        final String resource = getRequiredArg(properties, 
CommandOption.POLICY_RESOURCE);
+
+        AccessPolicy currentPolicy;
+        try {
+            currentPolicy = policiesClient.getAccessPolicy(action, resource);
+        } catch (NiFiRegistryException e) {
+            currentPolicy = null;
+        }
+
+        if (currentPolicy == null) {
+            currentPolicy = new AccessPolicy();
+
+            currentPolicy.setAction(action);
+            currentPolicy.setResource(resource);
+
+            setUsers(currentPolicy, properties, tenantsClient);
+            setGroups(currentPolicy, properties, tenantsClient);
+
+            policiesClient.createAccessPolicy(currentPolicy);
+        } else {
+            setUsers(currentPolicy, properties, tenantsClient);
+            setGroups(currentPolicy, properties, tenantsClient);
+
+            policiesClient.updateAccessPolicy(currentPolicy);
+        }
+
+        return VoidResult.getInstance();
+    }
+
+    private void setUsers(AccessPolicy accessPolicy, Properties properties, 
TenantsClient tenantsClient) throws IOException, NiFiRegistryException {
+        String userNames = getArg(properties, CommandOption.USER_NAME_LIST);
+        String userIds = getArg(properties, CommandOption.USER_ID_LIST);
+
+        if (StringUtils.isNotBlank(userNames) || 
StringUtils.isNotBlank(userIds)) {
+            Set<Tenant> existingUsers = 
TenantHelper.selectExistingTenants(userNames, userIds, 
tenantsClient.getUsers());
+
+            accessPolicy.setUsers(existingUsers);
+        }
+    }
+
+    private void setGroups(AccessPolicy accessPolicy, Properties properties, 
TenantsClient tenantsClient) throws IOException, NiFiRegistryException {
+        String groupNames = getArg(properties, CommandOption.GROUP_NAME_LIST);
+        String groupIds = getArg(properties, CommandOption.GROUP_ID_LIST);
+
+        if (StringUtils.isNotBlank(groupNames) || 
StringUtils.isNotBlank(groupIds)) {
+            Set<Tenant> existingGroups = 
TenantHelper.selectExistingTenants(groupNames, groupIds, 
tenantsClient.getUserGroups());
+
+            accessPolicy.setUserGroups(existingGroups);
+        }
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreatePolicy.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/GetAccessPolicy.java
similarity index 54%
rename from 
nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreatePolicy.java
rename to 
nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/GetAccessPolicy.java
index 5ab45aa..278e540 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreatePolicy.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/policy/GetAccessPolicy.java
@@ -14,84 +14,56 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.toolkit.cli.impl.command.registry.tenant;
+package org.apache.nifi.toolkit.cli.impl.command.registry.policy;
 
 import org.apache.commons.cli.ParseException;
 import org.apache.nifi.registry.authorization.AccessPolicy;
-import org.apache.nifi.registry.authorization.Tenant;
 import org.apache.nifi.registry.client.NiFiRegistryClient;
 import org.apache.nifi.registry.client.NiFiRegistryException;
 import org.apache.nifi.toolkit.cli.api.Context;
 import org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.PoliciesClient;
-import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
 import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
-import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.toolkit.cli.impl.result.registry.AccessPolicyResult;
 
 import java.io.IOException;
 import java.util.Properties;
-import java.util.Set;
 
 /**
- * Command for creating an access policy.
+ * Command to get the configuration of an access policy.
  */
-public class CreatePolicy extends AbstractNiFiRegistryCommand<StringResult> {
-
-    public CreatePolicy() {
-        super("create-policy", StringResult.class);
+public class GetAccessPolicy extends 
AbstractNiFiRegistryCommand<AccessPolicyResult> {
+    public GetAccessPolicy() {
+        super("get-policy", AccessPolicyResult.class);
     }
 
     @Override
     public String getDescription() {
-        return "Creates new access policy";
+        return "Retrieves the configuration for an access policy.";
     }
 
     @Override
     protected void doInitialize(final Context context) {
-        // Required
         addOption(CommandOption.POLICY_RESOURCE.createOption());
         addOption(CommandOption.POLICY_ACTION.createOption());
-
-        // Optional
-        addOption(CommandOption.USER_NAME_LIST.createOption());
-        addOption(CommandOption.USER_ID_LIST.createOption());
-        addOption(CommandOption.GROUP_NAME_LIST.createOption());
-        addOption(CommandOption.GROUP_ID_LIST.createOption());
     }
 
     @Override
-    public StringResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
-            throws IOException, NiFiRegistryException, ParseException {
-
+    public AccessPolicyResult doExecute(final NiFiRegistryClient client, final 
Properties properties) throws IOException, NiFiRegistryException, 
ParseException {
         if (!(client instanceof ExtendedNiFiRegistryClient)) {
             throw new IllegalArgumentException("This command needs extended 
registry client!");
         }
-
         final ExtendedNiFiRegistryClient extendedClient = 
(ExtendedNiFiRegistryClient) client;
+
         final PoliciesClient policiesClient = 
extendedClient.getPoliciesClient();
-        final TenantsClient tenantsClient = extendedClient.getTenantsClient();
 
-        final String resource = getRequiredArg(properties, 
CommandOption.POLICY_RESOURCE);
         final String action = getRequiredArg(properties, 
CommandOption.POLICY_ACTION);
+        final String resource = getRequiredArg(properties, 
CommandOption.POLICY_RESOURCE);
 
-        final Set<Tenant> users = TenantHelper.getExistingUsers(
-                tenantsClient,
-                getArg(properties, CommandOption.USER_NAME_LIST),
-                getArg(properties, CommandOption.USER_ID_LIST));
-
-        final Set<Tenant> userGroups = TenantHelper.getExistingGroups(
-                tenantsClient,
-                getArg(properties, CommandOption.GROUP_NAME_LIST),
-                getArg(properties, CommandOption.GROUP_ID_LIST));
-
-        final AccessPolicy policy = new AccessPolicy();
-        policy.setAction(action);
-        policy.setResource(resource);
-        policy.setUsers(users);
-        policy.setUserGroups(userGroups);
+        final AccessPolicy accessPolicy = 
policiesClient.getAccessPolicy(action, resource);
 
-        final AccessPolicy createdPolicy = policiesClient.createPolicy(policy);
-        return new StringResult(createdPolicy.getIdentifier(), 
getContext().isInteractive());
+        return new AccessPolicyResult(getResultType(properties), accessPolicy);
     }
+
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/AbstractListTenants.java
similarity index 60%
copy from 
nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
copy to 
nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/AbstractListTenants.java
index baf61f0..58b631f 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/AbstractListTenants.java
@@ -17,41 +17,31 @@
 package org.apache.nifi.toolkit.cli.impl.command.registry.tenant;
 
 import org.apache.commons.cli.ParseException;
-import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.registry.authorization.Tenant;
 import org.apache.nifi.registry.client.NiFiRegistryClient;
 import org.apache.nifi.registry.client.NiFiRegistryException;
-import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.api.Result;
 import org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
-import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
-import org.apache.nifi.toolkit.cli.impl.result.StringResult;
 
 import java.io.IOException;
 import java.util.Properties;
 
 /**
- * Command for creating a user.
+ * Abstract command to get the list of tenants of a specific type.
+ *
+ * @param <T> The type of tenants in the result.
+ * @param <R> The type of the result object.
  */
-public class CreateUser extends AbstractNiFiRegistryCommand<StringResult> {
-
-    public CreateUser() {
-        super("create-user", StringResult.class);
-    }
-
-    @Override
-    public String getDescription() {
-        return "Creates new user.";
+public abstract class AbstractListTenants<T extends Tenant, R extends Result> 
extends AbstractNiFiRegistryCommand<R> {
+    public AbstractListTenants(String name, Class<R> resultClass) {
+        super(name, resultClass);
     }
 
     @Override
-    protected void doInitialize(final Context context) {
-        addOption(CommandOption.USER_NAME.createOption());
-    }
-
-    @Override
-    public StringResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
-            throws IOException, NiFiRegistryException, ParseException {
+    public R doExecute(final NiFiRegistryClient client, final Properties 
properties)
+        throws IOException, NiFiRegistryException, ParseException {
 
         if (!(client instanceof ExtendedNiFiRegistryClient)) {
             throw new IllegalArgumentException("This command needs extended 
registry client!");
@@ -60,10 +50,8 @@ public class CreateUser extends 
AbstractNiFiRegistryCommand<StringResult> {
         final ExtendedNiFiRegistryClient extendedClient = 
(ExtendedNiFiRegistryClient) client;
         final TenantsClient tenantsClient = extendedClient.getTenantsClient();
 
-        final String userName = getRequiredArg(properties, 
CommandOption.USER_NAME);
-        final User user = new User(null, userName);
-        final User createdUser = tenantsClient.createUser(user);
-
-        return new StringResult(createdUser.getIdentifier(), 
getContext().isInteractive());
+        return getTenants(properties, tenantsClient);
     }
+
+    protected abstract R getTenants(Properties properties, TenantsClient 
tenantsClient) throws NiFiRegistryException, IOException;
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
index baf61f0..21ac38a 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUser.java
@@ -41,7 +41,7 @@ public class CreateUser extends 
AbstractNiFiRegistryCommand<StringResult> {
 
     @Override
     public String getDescription() {
-        return "Creates new user.";
+        return "Creates a new user.";
     }
 
     @Override
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUserGroup.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUserGroup.java
index fced142..212ba72 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUserGroup.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/CreateUserGroup.java
@@ -69,13 +69,14 @@ public class CreateUserGroup extends 
AbstractNiFiRegistryCommand<StringResult> {
 
         final String groupName = getRequiredArg(properties, 
CommandOption.UG_NAME);
 
-        final Set<Tenant> tenants = TenantHelper.getExistingUsers(
-                tenantsClient,
-                getArg(properties, CommandOption.USER_NAME_LIST),
-                getArg(properties, CommandOption.USER_ID_LIST));
+        Set<Tenant> users = TenantHelper.selectExistingTenants(
+            getArg(properties, CommandOption.USER_NAME_LIST),
+            getArg(properties, CommandOption.USER_ID_LIST),
+            tenantsClient.getUsers()
+        );
 
         final UserGroup group = new UserGroup(null, groupName);
-        group.setUsers(tenants);
+        group.setUsers(users);
 
         final UserGroup createdGroup = tenantsClient.createUserGroup(group);
         return new StringResult(createdGroup.getIdentifier(), 
getContext().isInteractive());
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUserGroups.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUserGroups.java
new file mode 100644
index 0000000..c8dd38e
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUserGroups.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.toolkit.cli.impl.command.registry.tenant;
+
+import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
+import org.apache.nifi.toolkit.cli.impl.result.registry.UserGroupsResult;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Command to get the list of user groups.
+ */
+public class ListUserGroups extends AbstractListTenants<UserGroup, 
UserGroupsResult> {
+    public ListUserGroups() {
+        super("list-user-groups", UserGroupsResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Retrieves the list of user group.";
+    }
+
+    @Override
+    protected UserGroupsResult getTenants(Properties properties, TenantsClient 
tenantsClient) throws NiFiRegistryException, IOException {
+        List<UserGroup> userGroups = tenantsClient.getUserGroups();
+
+        return new UserGroupsResult(getResultType(properties), userGroups);
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUsers.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUsers.java
new file mode 100644
index 0000000..52877ab
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/ListUsers.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.toolkit.cli.impl.command.registry.tenant;
+
+import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
+import org.apache.nifi.toolkit.cli.impl.result.registry.UsersResult;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Command to get the list of users.
+ */
+public class ListUsers extends AbstractListTenants<User, UsersResult> {
+    public ListUsers() {
+        super("list-users", UsersResult.class);
+    }
+
+    @Override
+    public String getDescription() {
+        return "Retrieves the list of user.";
+    }
+
+    @Override
+    protected UsersResult getTenants(Properties properties, TenantsClient 
tenantsClient) throws NiFiRegistryException, IOException {
+        List<User> users = tenantsClient.getUsers();
+
+        return new UsersResult(getResultType(properties), users);
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TenantHelper.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TenantHelper.java
index 64415f9..97a2df1 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TenantHelper.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TenantHelper.java
@@ -16,78 +16,28 @@
  */
 package org.apache.nifi.toolkit.cli.impl.command.registry.tenant;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.authorization.Tenant;
-import org.apache.nifi.registry.authorization.User;
-import org.apache.nifi.registry.authorization.UserGroup;
-import org.apache.nifi.registry.client.NiFiRegistryException;
-import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
 
-import java.io.IOException;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
-final class TenantHelper {
-    private static final String SEPARATOR = ",";
+public final class TenantHelper {
+    private TenantHelper() {}
 
-    private TenantHelper() {
-        // no op
-    }
-
-    public static Set<Tenant> getExistingUsers(final TenantsClient client, 
final String userNamesArgument, final String userIdsArgument)
-            throws IOException, NiFiRegistryException {
-        final Set<Tenant> result = new HashSet<>();
-
-        final Set<String> userNames = StringUtils.isNotBlank(userNamesArgument)
-                ? new 
HashSet<>(Arrays.asList(userNamesArgument.split(SEPARATOR)))
-                : Collections.emptySet();
-
-        final Set<String> userIds = StringUtils.isNotBlank(userIdsArgument)
-                ? new 
HashSet<>(Arrays.asList(userIdsArgument.split(SEPARATOR)))
-                : Collections.emptySet();
-
-        if (userNames.isEmpty() && userIds.isEmpty()) {
-            return result;
-        }
-
-        final List<User> users = client.getUsers();
-
-        for (final User user : users) {
-            if (userNames.contains(user.getIdentity()) || 
userIds.contains(user.getIdentifier())) {
-                result.add(user);
-            }
-        }
-
-        return result;
-    }
-
-    public static Set<Tenant> getExistingGroups(final TenantsClient client, 
final String userGroupNamesArgument, final String userGroupIdsArgument)
-            throws IOException, NiFiRegistryException {
-        final Set<Tenant> result = new HashSet<>();
-
-        final Set<String> userGroupNames = 
StringUtils.isNotBlank(userGroupNamesArgument)
-                ? new 
HashSet<>(Arrays.asList(userGroupNamesArgument.split(SEPARATOR)))
-                : Collections.emptySet();
-
-        final Set<String> userGroupIds = 
StringUtils.isNotBlank(userGroupIdsArgument)
-                ? new 
HashSet<>(Arrays.asList(userGroupIdsArgument.split(SEPARATOR)))
-                : Collections.emptySet();
-
-        if (userGroupNames.isEmpty() && userGroupIds.isEmpty()) {
-            return result;
-        }
+    public static <T extends Tenant> Set<Tenant> selectExistingTenants(final 
String names, final String ids, List<T> allTenants) {
+        String separator = ",";
 
-        final List<UserGroup> usersGroups = client.getUserGroups();
+        Set<String> nameSet = new 
HashSet<>(Arrays.asList(Optional.ofNullable(names).orElse("").split(separator)));
+        Set<String> idSet = new 
HashSet<>(Arrays.asList(Optional.ofNullable(ids).orElse("").split(separator)));
 
-        for (final UserGroup userGroup : usersGroups) {
-            if (userGroupNames.contains(userGroup.getIdentity()) || 
userGroupIds.contains(userGroup.getIdentifier())) {
-                result.add(userGroup);
-            }
-        }
+        Set<Tenant> existingTentants = allTenants.stream()
+            .filter(tenant -> nameSet.contains(tenant.getIdentity()) || 
idSet.contains(tenant.getIdentifier()))
+            .collect(Collectors.toSet());
 
-        return result;
+        return existingTentants;
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdatePolicy.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdatePolicy.java
deleted file mode 100644
index f30f3cf..0000000
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdatePolicy.java
+++ /dev/null
@@ -1,112 +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.toolkit.cli.impl.command.registry.tenant;
-
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.registry.authorization.AccessPolicy;
-import org.apache.nifi.registry.authorization.Tenant;
-import org.apache.nifi.registry.client.NiFiRegistryClient;
-import org.apache.nifi.registry.client.NiFiRegistryException;
-import org.apache.nifi.toolkit.cli.api.Context;
-import org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
-import org.apache.nifi.toolkit.cli.impl.client.registry.PoliciesClient;
-import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
-import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
-import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
-import org.apache.nifi.toolkit.cli.impl.result.StringResult;
-
-import java.io.IOException;
-import java.util.Properties;
-import java.util.Set;
-
-/**
- * Command for update an existing policy.
- */
-public class UpdatePolicy extends AbstractNiFiRegistryCommand<StringResult> {
-
-    public UpdatePolicy() {
-        super("update-policy", StringResult.class);
-    }
-
-    @Override
-    public String getDescription() {
-        return "Updates an existing access policy.";
-    }
-
-    @Override
-    protected void doInitialize(final Context context) {
-        // Required
-        addOption(CommandOption.POLICY_ID.createOption());
-
-        // Optional
-        addOption(CommandOption.POLICY_RESOURCE.createOption());
-        addOption(CommandOption.POLICY_ACTION.createOption());
-        addOption(CommandOption.USER_NAME_LIST.createOption());
-        addOption(CommandOption.USER_ID_LIST.createOption());
-        addOption(CommandOption.GROUP_NAME_LIST.createOption());
-        addOption(CommandOption.GROUP_ID_LIST.createOption());
-    }
-
-    @Override
-    public StringResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
-            throws IOException, NiFiRegistryException, ParseException {
-
-        if (!(client instanceof ExtendedNiFiRegistryClient)) {
-            throw new IllegalArgumentException("This command needs extended 
registry client!");
-        }
-
-        final ExtendedNiFiRegistryClient extendedClient = 
(ExtendedNiFiRegistryClient) client;
-        final PoliciesClient policiesClient = 
extendedClient.getPoliciesClient();
-        final TenantsClient tenantsClient = extendedClient.getTenantsClient();
-
-        final String policyId = getRequiredArg(properties, 
CommandOption.POLICY_ID);
-        final String resource = getArg(properties, 
CommandOption.POLICY_RESOURCE);
-        final String action = getArg(properties, CommandOption.POLICY_ACTION);
-
-        final AccessPolicy existingPolicy = policiesClient.getPolicy(policyId);
-
-        if (StringUtils.isNotBlank(resource)) {
-            existingPolicy.setResource(resource);
-        }
-
-        if (StringUtils.isNotBlank(action)) {
-            existingPolicy.setAction(action);
-        }
-
-        final Set<Tenant> users = TenantHelper.getExistingUsers(
-                tenantsClient,
-                getArg(properties, CommandOption.USER_NAME_LIST),
-                getArg(properties, CommandOption.USER_ID_LIST));
-
-        if (StringUtils.isNotBlank(getArg(properties, 
CommandOption.USER_NAME_LIST)) || StringUtils.isNotBlank(getArg(properties, 
CommandOption.USER_ID_LIST))) {
-            existingPolicy.setUsers(users);
-        }
-
-        final Set<Tenant> userGroups = TenantHelper.getExistingGroups(
-                tenantsClient,
-                getArg(properties, CommandOption.GROUP_NAME_LIST),
-                getArg(properties, CommandOption.GROUP_ID_LIST));
-
-        if (StringUtils.isNotBlank(getArg(properties, 
CommandOption.GROUP_NAME_LIST)) || StringUtils.isNotBlank(getArg(properties, 
CommandOption.GROUP_ID_LIST))) {
-            existingPolicy.setUserGroups(userGroups);
-        }
-
-        final AccessPolicy updatedPolicy = 
policiesClient.updatePolicy(existingPolicy);
-        return new StringResult(updatedPolicy.getIdentifier(), 
getContext().isInteractive());
-    }
-}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUser.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUser.java
index c41f1ba..d2c8a05 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUser.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUser.java
@@ -26,18 +26,17 @@ import 
org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
 import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
-import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
 
 import java.io.IOException;
 import java.util.Properties;
 
 /**
- * Command for update an existing user.
+ * Command for updating an existing user.
  */
-public class UpdateUser extends AbstractNiFiRegistryCommand<StringResult> {
-
+public class UpdateUser extends AbstractNiFiRegistryCommand<VoidResult> {
     public UpdateUser() {
-        super("update-user", StringResult.class);
+        super("update-user", VoidResult.class);
     }
 
     @Override
@@ -55,7 +54,7 @@ public class UpdateUser extends 
AbstractNiFiRegistryCommand<StringResult> {
     }
 
     @Override
-    public StringResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
+    public VoidResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
         throws IOException, NiFiRegistryException, ParseException {
 
         if (!(client instanceof ExtendedNiFiRegistryClient)) {
@@ -73,7 +72,8 @@ public class UpdateUser extends 
AbstractNiFiRegistryCommand<StringResult> {
             existingUser.setIdentity(userName);
         }
 
-        final User updatedUser = tenantsClient.updateUser(existingUser);
-        return new StringResult(updatedUser.getIdentifier(), 
getContext().isInteractive());
+        tenantsClient.updateUser(existingUser);
+
+        return VoidResult.getInstance();
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUserGroup.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUserGroup.java
index d29a843..45b0ff2 100644
--- 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUserGroup.java
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/UpdateUserGroup.java
@@ -27,7 +27,7 @@ import 
org.apache.nifi.toolkit.cli.impl.client.ExtendedNiFiRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.client.registry.TenantsClient;
 import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
 import 
org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand;
-import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
 
 import java.io.IOException;
 import java.util.Properties;
@@ -36,10 +36,10 @@ import java.util.Set;
 /**
  * Command for update an existing user group.
  */
-public class UpdateUserGroup extends AbstractNiFiRegistryCommand<StringResult> 
{
+public class UpdateUserGroup extends AbstractNiFiRegistryCommand<VoidResult> {
 
     public UpdateUserGroup() {
-        super("update-user-group", StringResult.class);
+        super("update-user-group", VoidResult.class);
     }
 
     @Override
@@ -59,7 +59,7 @@ public class UpdateUserGroup extends 
AbstractNiFiRegistryCommand<StringResult> {
     }
 
     @Override
-    public StringResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
+    public VoidResult doExecute(final NiFiRegistryClient client, final 
Properties properties)
             throws IOException, NiFiRegistryException, ParseException {
         if (!(client instanceof ExtendedNiFiRegistryClient)) {
             throw new IllegalArgumentException("This command needs extended 
registry client!");
@@ -78,14 +78,16 @@ public class UpdateUserGroup extends 
AbstractNiFiRegistryCommand<StringResult> {
         }
 
         // Update group members
-        final Set<Tenant> tenants = TenantHelper.getExistingUsers(
-                tenantsClient,
-                getArg(properties, CommandOption.USER_NAME_LIST),
-                getArg(properties, CommandOption.USER_ID_LIST));
+        Set<Tenant> users = TenantHelper.selectExistingTenants(
+            getArg(properties, CommandOption.USER_NAME_LIST),
+            getArg(properties, CommandOption.USER_ID_LIST),
+            tenantsClient.getUsers()
+        );
 
-        existingGroup.setUsers(tenants);
+        existingGroup.setUsers(users);
 
-        final UserGroup updatedGroup = 
tenantsClient.updateUserGroup(existingGroup);
-        return new StringResult(updatedGroup.getIdentifier(), 
getContext().isInteractive());
+        tenantsClient.updateUserGroup(existingGroup);
+
+        return VoidResult.getInstance();
     }
 }
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/AccessPolicyResult.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/AccessPolicyResult.java
new file mode 100644
index 0000000..c394601
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/AccessPolicyResult.java
@@ -0,0 +1,59 @@
+/*
+ * 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.toolkit.cli.impl.result.registry;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.registry.authorization.AccessPolicy;
+import org.apache.nifi.registry.authorization.Tenant;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class AccessPolicyResult extends AbstractWritableResult<AccessPolicy> {
+    private final AccessPolicy accessPolicy;
+
+    public AccessPolicyResult(ResultType resultType, AccessPolicy 
accessPolicy) {
+        super(resultType);
+        this.accessPolicy = accessPolicy;
+        Validate.notNull(accessPolicy);
+    }
+
+    @Override
+    public AccessPolicy getResult() {
+        return accessPolicy;
+    }
+
+    @Override
+    protected void writeSimpleResult(PrintStream output) throws IOException {
+        output.printf("Resource: %s\nAction  : %s\nUsers   : %s\nGroups  : 
%s\n",
+            accessPolicy.getResource(),
+            accessPolicy.getAction(),
+            joinTenantIdentities(accessPolicy.getUsers()),
+            joinTenantIdentities(accessPolicy.getUserGroups())
+        );
+    }
+
+    private String joinTenantIdentities(Set<Tenant> tenants) {
+        return tenants.stream()
+            .map(tenant -> tenant.getIdentity())
+            .collect(Collectors.joining(", "));
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UserGroupsResult.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UserGroupsResult.java
new file mode 100644
index 0000000..d517cef
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UserGroupsResult.java
@@ -0,0 +1,82 @@
+/*
+ * 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.toolkit.cli.impl.result.registry;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.registry.authorization.Tenant;
+import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Result for a list of users.
+ */
+public class UserGroupsResult extends AbstractWritableResult<List<UserGroup>> {
+    private final List<UserGroup> userGroups;
+
+    public UserGroupsResult(final ResultType resultType, final List<UserGroup> 
userGroups) {
+        super(resultType);
+        this.userGroups = userGroups;
+        Validate.notNull(userGroups);
+    }
+
+    @Override
+    public List<UserGroup> getResult() {
+        return userGroups;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        if (userGroups.isEmpty()) {
+            return;
+        }
+
+        final Table table = new Table.Builder()
+            .column("#", 3, 3, false)
+            .column("Name", 20, 36, true)
+            .column("Id", 36, 36, false)
+            .column("Users", 36, 200, false)
+            .build();
+
+        for (int userIndex = 0; userIndex < userGroups.size(); ++userIndex) {
+            final UserGroup userGroup = userGroups.get(userIndex);
+            table.addRow(
+                String.valueOf(userIndex + 1),
+                userGroup.getIdentity(),
+                userGroup.getIdentifier(),
+                joinTenantIdentities(userGroup.getUsers())
+            );
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+    private String joinTenantIdentities(Set<Tenant> tenants) {
+        return tenants.stream()
+            .map(tenant -> tenant.getIdentity())
+            .collect(Collectors.joining("; "));
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UsersResult.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UsersResult.java
new file mode 100644
index 0000000..4d19a3b
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/registry/UsersResult.java
@@ -0,0 +1,67 @@
+/*
+ * 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.toolkit.cli.impl.result.registry;
+
+import org.apache.commons.lang3.Validate;
+import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * Result for a list of users.
+ */
+public class UsersResult extends AbstractWritableResult<List<User>> {
+    private final List<User> users;
+
+    public UsersResult(ResultType resultType, List<User> users) {
+        super(resultType);
+        this.users = users;
+        Validate.notNull(users);
+    }
+
+    @Override
+    public List<User> getResult() {
+        return users;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        if (users.isEmpty()) {
+            return;
+        }
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Name", 20, 36, true)
+                .column("Id", 36, 36, false)
+                .build();
+
+        for (int userIndex = 0; userIndex < users.size(); ++userIndex) {
+            final User user = users.get(userIndex);
+            table.addRow(String.valueOf(userIndex + 1), user.getIdentity(), 
user.getIdentifier());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/RegistryManualIT.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/RegistryManualIT.java
new file mode 100644
index 0000000..a8e0e5c
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/RegistryManualIT.java
@@ -0,0 +1,311 @@
+/*
+ * 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.toolkit.cli.impl.command.registry;
+
+import org.apache.nifi.toolkit.cli.CLIMain;
+import org.apache.nifi.util.StringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.StringJoiner;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class RegistryManualIT {
+    public static final String TRUSTSTORE = "";
+    public static final String TRUSTSTORE_PASSWD = "";
+
+    public static final String KEYSTORE = "";
+    public static final String KEYSTORE_PASSWD = "";
+
+    private String basicSettings = "--baseUrl https://localhost:18443 
--verbose";
+    private String securitySettings = "--truststore " + TRUSTSTORE +
+        " --truststoreType jks --truststorePasswd " + TRUSTSTORE_PASSWD +
+        " --keystore " + KEYSTORE +
+        " --keystorePasswd " + KEYSTORE_PASSWD +
+        " --keystoreType PKCS12";
+
+    private static final String TEST_USER_NAME = "testUser";
+    private static final String TEST_USER_GROUP_NAME = "testUserGroup";
+    private String testUserId;
+    private String testUserGroupId;
+
+    private PrintStream originalStdOut;
+    private ByteArrayOutputStream out;
+
+    @Before
+    public void setUp() throws Exception {
+        assertFalse("truststore not set", StringUtils.isBlank(TRUSTSTORE));
+        assertFalse("truststorePasswd not set", 
StringUtils.isBlank(TRUSTSTORE_PASSWD));
+        assertFalse("keystore not set", StringUtils.isBlank(KEYSTORE));
+        assertFalse("keystorePassed not set", 
StringUtils.isBlank(KEYSTORE_PASSWD));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        resetStdOut();
+    }
+
+    @Ignore("Run first and only once")
+    @Test
+    public void testCreateUser() throws Exception {
+        String userName = TEST_USER_NAME;
+
+        runRegistryCommand("create-user",
+            "--userName " + userName
+        );
+
+        testListUsers(userName);
+    }
+
+    @Ignore("Run first and only once")
+    @Test
+    public void testCreateUseGroup() throws Exception {
+        String expectedUserGroup = TEST_USER_GROUP_NAME;
+
+        runRegistryCommand("create-user-group",
+            "--userGroupName " + expectedUserGroup +
+                " --userNameList " + TEST_USER_NAME +
+                " --userIdList " + testUserId
+        );
+
+       testListUserGroup(expectedUserGroup);
+    }
+
+    @Test
+    public void testListUsers() throws Exception {
+        testListUsers(TEST_USER_NAME);
+    }
+
+    @Test
+    public void testUpdateUser() throws Exception {
+        String originalUserName = TEST_USER_NAME;
+        String updatedUserName = "updatedTestUser";
+
+        testListUsers(originalUserName);
+
+        runRegistryCommand("update-user",
+            "--userName " + updatedUserName +
+                " --userId " + testUserId
+        );
+
+        testListUsers(updatedUserName);
+
+        runRegistryCommand("update-user",
+            "--userName " + originalUserName +
+                " --userId " + testUserId
+        );
+
+        testListUsers(originalUserName);
+    }
+
+    @Test
+    public void testListUserGroups() throws Exception {
+        testListUserGroup(TEST_USER_GROUP_NAME);
+    }
+
+    @Test
+    public void testUpdateUserGroup() throws Exception {
+        runRegistryCommand("update-user-group",
+            "--userGroupName " + TEST_USER_GROUP_NAME +
+                " --userNameList " + TEST_USER_NAME +
+                " --userIdLis " + testUserId +
+                " --userGroupId " + testUserGroupId
+        );
+
+        testListUserGroup(TEST_USER_GROUP_NAME);
+    }
+
+    @Test
+    public void testGetAccessPolicy() throws Exception {
+        String action = "read";
+        String resource = "/testResource";
+
+        testGetAccessPolicy(action, resource);
+    }
+
+    @Test
+    public void testCreateOrUpdateAccessPolicy() throws Exception {
+        String action = "read";
+        String resource = "/testResource";
+
+        runRegistryCommand("update-policy",
+            "--accessPolicyResource " + resource +
+                " --accessPolicyAction " + action +
+                " --userNameList " + TEST_USER_NAME +
+                " --userIdList " + testUserId +
+                " --groupNameList " + TEST_USER_GROUP_NAME +
+                " --groupIdList " + testUserGroupId
+        );
+
+        testGetAccessPolicy(action, resource);
+
+        // Users are not removed if none is specified
+        runRegistryCommand("update-policy",
+            "--accessPolicyResource " + resource +
+                " --accessPolicyAction " + action +
+                " --groupNameList " + TEST_USER_GROUP_NAME +
+                " --groupIdList " + testUserGroupId
+        );
+
+        testGetAccessPolicy(action, resource);
+
+        // Groups are not removed if none is specified
+        runRegistryCommand("update-policy",
+            "--accessPolicyResource " + resource +
+                " --accessPolicyAction " + action +
+                " --userNameList " + TEST_USER_NAME +
+                " --userIdList " + testUserId
+        );
+
+        testGetAccessPolicy(action, resource);
+    }
+
+    private void testListUsers(String expectedUserName) throws IOException {
+        runCommand(
+            "\\s{3,}",
+            () -> runRegistryCommand("list-users", ""),
+            words -> {
+                if (words.length > 2 && words[1].equals(expectedUserName)) {
+                    testUserId = words[2];
+                }
+            },
+            () -> {
+                assertNotNull(testUserId);
+                assertTrue("User id shouldn't be blank!", 
!StringUtils.isBlank(testUserId));
+            }
+        );
+    }
+
+    private void testListUserGroup(String expectedUserGroupName) throws 
IOException {
+        runCommand(
+            "\\s{3,}",
+            () -> runRegistryCommand("list-user-groups", ""),
+            words -> {
+                if (words.length > 3 && 
words[1].equals(expectedUserGroupName)) {
+                    testUserGroupId = words[2];
+                }
+            },
+            () -> {
+                assertNotNull(testUserGroupId);
+                assertTrue("Group id shouldn't be blank!", 
!StringUtils.isBlank(testUserGroupId));
+            }
+        );
+    }
+
+    private void testGetAccessPolicy(String action, String resource) throws 
IOException {
+        AtomicReference<String> resourceR = new AtomicReference<>();
+        AtomicReference<String> actionR = new AtomicReference<>();
+        AtomicReference<String> usersR = new AtomicReference<>();
+        AtomicReference<String> groupsR = new AtomicReference<>();
+
+        runCommand(
+            "\\s*:\\s+",
+            () -> runRegistryCommand("get-policy",
+                "--accessPolicyResource " + resource +
+                    " --accessPolicyAction " + action
+            ),
+            words -> {
+                if (words.length > 1) {
+                    if (words[0].equals("Users")) {
+                        usersR.set(words[1]);
+                    } else if (words[0].equals("Groups")) {
+                        groupsR.set(words[1]);
+                    } else if (words[0].equals("Resource")) {
+                        resourceR.set(words[1]);
+                    } else if (words[0].equals("Action")) {
+                        actionR.set(words[1]);
+                    }
+                }
+            },
+            () -> {
+                resourceR.get().equals(resource);
+                actionR.get().contains(action);
+                usersR.get().contains(TEST_USER_NAME);
+                groupsR.get().contains(TEST_USER_GROUP_NAME);
+            }
+        );
+    }
+
+    private void runCommand(String delimiter, Runnable commandRunner, 
Consumer<String[]> outputLineHandler, Runnable check) throws IOException {
+        try {
+            // GIVEN
+            startCaptureStdOut();
+
+            // WHEN
+            commandRunner.run();
+
+            // THEN
+            BufferedReader bufferedOutput = new BufferedReader(new 
InputStreamReader(new ByteArrayInputStream(out.toByteArray())));
+
+            String line;
+            while ((line = bufferedOutput.readLine()) != null) {
+                originalStdOut.println(line);
+
+                String[] words = line.split(delimiter);
+
+                outputLineHandler.accept(words);
+            }
+
+            check.run();
+        } finally {
+            resetStdOut();
+        }
+    }
+
+    private void runRegistryCommand(String command, String commandArguments) {
+        // GIVEN
+        String arguments = new StringJoiner(" ")
+            .add("registry")
+            .add(command)
+            .add(commandArguments)
+            .add(basicSettings)
+            .add(securitySettings)
+            .toString();
+
+        String[] args = arguments.split("\\s+");
+
+        // WHEN
+        CLIMain.runSingleCommand(args);
+
+        // THEN
+    }
+
+    private void startCaptureStdOut() {
+        originalStdOut = System.out;
+        out = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(out));
+    }
+
+    private void resetStdOut() {
+        if (originalStdOut != null) {
+            System.setOut(originalStdOut);
+        }
+    }
+}
diff --git 
a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TestTenantHelper.java
 
b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TestTenantHelper.java
new file mode 100644
index 0000000..7efe903
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/registry/tenant/TestTenantHelper.java
@@ -0,0 +1,153 @@
+/*
+ * 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.toolkit.cli.impl.command.registry.tenant;
+
+import org.apache.nifi.registry.authorization.Tenant;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+public class TestTenantHelper {
+    @Test
+    public void testSelectExistingTenantsWithEmptyNamesAndIds() throws 
Exception {
+        // GIVEN
+        String names = "";
+        String ids = "";
+
+        List<Tenant> allTenants = Arrays.asList(
+            createTenant("__1", "__1"),
+            createTenant("__2", "__2")
+        );
+
+        List<Tenant> expected = Collections.emptyList();
+
+        // WHEN
+        // THEN
+        testSelectExistingTenants(names, ids, allTenants, expected);
+    }
+
+    @Test
+    public void testSelectExistingTenantsWithNames() throws Exception {
+        // GIVEN
+        String names = "name1,name3";
+        String ids = "";
+
+        Tenant tenantFoundByName1 = createTenant("__1", "name1");
+        Tenant tenantNotFound2 = createTenant("__2", "__2");
+        Tenant tenantFoundByName3 = createTenant("__3", "name3");
+        Tenant tenantNotFound4 = createTenant("__4", "__4");
+
+        List<Tenant> allTenants = Arrays.asList(
+            tenantFoundByName1,
+            tenantNotFound2,
+            tenantFoundByName3,
+            tenantNotFound4
+        );
+
+        List<Tenant> expected = Arrays.asList(
+            tenantFoundByName1,
+            tenantFoundByName3
+        );
+
+        // WHEN
+        // THEN
+        testSelectExistingTenants(names, ids, allTenants, expected);
+    }
+
+    @Test
+    public void testSelectExistingTenantsWithIds() throws Exception {
+        // GIVEN
+        String names = "";
+        String ids = "id1,id2";
+
+        Tenant tenantFoundById1 = createTenant("id1", "__1");
+        Tenant tenantFoundById2 = createTenant("id2", "__2");
+        Tenant tenantNotFound3 = createTenant("__3", "__3");
+        Tenant tenantNotFound4 = createTenant("__4", "__4");
+
+        List<Tenant> allTenants = Arrays.asList(
+            tenantFoundById1,
+            tenantFoundById2,
+            tenantNotFound3,
+            tenantNotFound4
+        );
+
+        List<Tenant> expected = Arrays.asList(
+            tenantFoundById1,
+            tenantFoundById2
+        );
+
+        // WHEN
+        // THEN
+        testSelectExistingTenants(names, ids, allTenants, expected);
+    }
+
+    @Test
+    public void testSelectExistingTenantsWithComplexScenario() throws 
Exception {
+        // GIVEN
+        String names = "name1,name3";
+        String ids = "id1,id2";
+
+        Tenant tenantFoundByIdAndName = createTenant("id1", "name1");
+        Tenant tenantFoundById = createTenant("id2", "_2_");
+        Tenant tenantFoundByName = createTenant("__3", "name3");
+        Tenant tenantNotFound = createTenant("__4", "__4");
+
+        List<Tenant> allTenants = Arrays.asList(
+            tenantFoundByIdAndName,
+            tenantFoundById,
+            tenantFoundByName,
+            tenantNotFound
+        );
+
+        List<Tenant> expected = Arrays.asList(
+            tenantFoundByIdAndName,
+            tenantFoundById,
+            tenantFoundByName
+        );
+
+        // WHEN
+        // THEN
+        testSelectExistingTenants(names, ids, allTenants, expected);
+    }
+
+    private void testSelectExistingTenants(String names, String ids, 
List<Tenant> allTenants, List<Tenant> expectedTenants) {
+        // GIVEN
+        Set<Tenant> expected = new HashSet<>(expectedTenants);
+
+        // WHEN
+        Set<Tenant> actual = TenantHelper.selectExistingTenants(names, ids, 
allTenants);
+
+        // THEN
+        assertEquals(expected, actual);
+    }
+
+    private Tenant createTenant(String identifier, String identity) {
+        Tenant tenant = new Tenant();
+
+        tenant.setIdentifier(identifier);
+        tenant.setIdentity(identity);
+
+        return tenant;
+    }
+}

Reply via email to