NIFIREG-146 REST API Documentation improvements

Improves the REST API documentation and swagger spec, including:

- Corrects handling of collection response types in REST API docs
- Adds required access policy information in REST API docs
- Adds missing required=true tags to swagger spec
- Adds missing readOnly-true tags to swagger spec
- Adds security definitions to swagger spec
- Corrects VersionedConnection.zIndex field name in swagger spec

Functionality changes:

- Adds authorization check to the getFlowDiff endpoint

This closes #103.

Signed-off-by: Bryan Bende <bbende@apache.org>
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
index 2939b91..525eb19 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
@@ -21,7 +21,7 @@
 import io.swagger.annotations.ApiModelProperty;
 
 /**
- * Access policy summary of which actions ("read', "write") are allowable for a specified web resource.
+ * Access policy summary of which actions ("read', "write", "delete") are allowable for a specified web resource.
  */
 @ApiModel("accessPolicySummary")
 public class AccessPolicySummary {
@@ -31,7 +31,7 @@
     private String action;
     private Boolean configurable;
 
-    @ApiModelProperty("The id of the policy. Set by server at creation time.")
+    @ApiModelProperty(value = "The id of the policy. Set by server at creation time.", readOnly = true)
     public String getIdentifier() {
         return identifier;
     }
@@ -40,7 +40,8 @@
         this.identifier = identifier;
     }
 
-    @ApiModelProperty("The resource for this access policy.")
+    @ApiModelProperty(value = "The resource for this access policy.", required = true
+    )
     public String getResource() {
         return resource;
     }
@@ -51,7 +52,8 @@
 
     @ApiModelProperty(
             value = "The action associated with this access policy.",
-            allowableValues = "READ, WRITE"
+            allowableValues = "read, write, delete",
+            required = true
     )
     public String getAction() {
         return action;
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/CurrentUser.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/CurrentUser.java
index 7b9b4ee..0d21c62 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/CurrentUser.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/CurrentUser.java
@@ -26,7 +26,7 @@
     private boolean anonymous;
     private ResourcePermissions resourcePermissions;
 
-    @ApiModelProperty("The identity of the current user")
+    @ApiModelProperty(value = "The identity of the current user", readOnly = true)
     public String getIdentity() {
         return identity;
     }
@@ -35,7 +35,7 @@
         this.identity = identity;
     }
 
-    @ApiModelProperty("Indicates if the current user is anonymous")
+    @ApiModelProperty(value = "Indicates if the current user is anonymous", readOnly = true)
     public boolean isAnonymous() {
         return anonymous;
     }
@@ -44,7 +44,7 @@
         this.anonymous = anonymous;
     }
 
-    @ApiModelProperty("The access that the current user has to top level resources")
+    @ApiModelProperty(value = "The access that the current user has to top level resources", readOnly = true)
     public ResourcePermissions getResourcePermissions() {
         return resourcePermissions;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Resource.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Resource.java
index 139d729..7dd4493 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Resource.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Resource.java
@@ -30,7 +30,7 @@
      *
      * @return The name of the resource
      */
-    @ApiModelProperty("The name of the resource.")
+    @ApiModelProperty(value = "The name of the resource.", readOnly = true)
     public String getName() {
         return name;
     }
@@ -44,7 +44,7 @@
      *
      * @return The identifier of the resource
      */
-    @ApiModelProperty("The identifier of the resource.")
+    @ApiModelProperty(value = "The identifier of the resource.", readOnly = true)
     public String getIdentifier() {
         return identifier;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/ResourcePermissions.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/ResourcePermissions.java
index 78cd10e..80e95c0 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/ResourcePermissions.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/ResourcePermissions.java
@@ -27,7 +27,9 @@
     private Permissions policies = new Permissions();
     private Permissions proxy = new Permissions();
 
-    @ApiModelProperty("The access that the current user has to any top level resources (a logical 'OR' of all other values)")
+    @ApiModelProperty(
+            value = "The access that the current user has to any top level resources (a logical 'OR' of all other values)",
+            readOnly = true)
     public Permissions getAnyTopLevelResource() {
         return new Permissions()
                 .withCanRead(buckets.getCanRead()
@@ -44,7 +46,9 @@
                         || proxy.getCanDelete());
     }
 
-    @ApiModelProperty("The access that the current user has to the top level /buckets resource of this NiFi Registry (i.e., access to all buckets)")
+    @ApiModelProperty(
+            value = "The access that the current user has to the top level /buckets resource of this NiFi Registry (i.e., access to all buckets)",
+            readOnly = true)
     public Permissions getBuckets() {
         return buckets;
     }
@@ -53,7 +57,9 @@
         this.buckets = buckets;
     }
 
-    @ApiModelProperty("The access that the current user has to the top level /tenants resource of this NiFi Registry")
+    @ApiModelProperty(
+            value = "The access that the current user has to the top level /tenants resource of this NiFi Registry",
+            readOnly = true)
     public Permissions getTenants() {
         return tenants;
     }
@@ -62,7 +68,9 @@
         this.tenants = tenants;
     }
 
-    @ApiModelProperty("The access that the current user has to the top level /policies resource of this NiFi Registry")
+    @ApiModelProperty(
+            value = "The access that the current user has to the top level /policies resource of this NiFi Registry",
+            readOnly = true)
     public Permissions getPolicies() {
         return policies;
     }
@@ -71,7 +79,9 @@
         this.policies = policies;
     }
 
-    @ApiModelProperty("The access that the current user has to the top level /proxy resource of this NiFi Registry")
+    @ApiModelProperty(
+            value = "The access that the current user has to the top level /proxy resource of this NiFi Registry",
+            readOnly = true)
     public Permissions getProxy() {
         return proxy;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
index 68c189e..19eee90 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
@@ -45,7 +45,9 @@
     /**
      * @return tenant's unique identifier
      */
-    @ApiModelProperty(value = "The computer-generated identifier of the tenant.", readOnly = true)
+    @ApiModelProperty(
+            value = "The computer-generated identifier of the tenant.",
+            readOnly = true)
     public String getIdentifier() {
         return identifier;
     }
@@ -57,7 +59,9 @@
     /**
      * @return tenant's identity
      */
-    @ApiModelProperty(value = "The human-facing identity of the tenant. This can only be changed if the tenant is configurable.")
+    @ApiModelProperty(
+            value = "The human-facing identity of the tenant. This can only be changed if the tenant is configurable.",
+            required = true)
     public String getIdentity() {
         return identity;
     }
@@ -66,7 +70,9 @@
         this.identity = identity;
     }
 
-    @ApiModelProperty(value = "Indicates if this tenant is configurable, based on which UserGroupProvider has been configured to manage it.", readOnly = true)
+    @ApiModelProperty(
+            value = "Indicates if this tenant is configurable, based on which UserGroupProvider has been configured to manage it.",
+            readOnly = true)
     public Boolean getConfigurable() {
         return configurable;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
index 1467929..570502d 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
@@ -30,7 +30,6 @@
 public class UserGroup extends Tenant {
 
     private Set<Tenant> users;
-    private Set<AccessPolicySummary> accessPolicies;
 
     public UserGroup() {}
 
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
index 3c6a59d..94402a9 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
@@ -18,8 +18,8 @@
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import org.apache.nifi.registry.link.LinkableEntity;
 import org.apache.nifi.registry.authorization.Permissions;
+import org.apache.nifi.registry.link.LinkableEntity;
 
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotBlank;
@@ -43,7 +43,7 @@
 
     private Permissions permissions;
 
-    @ApiModelProperty("An ID to uniquely identify this object.")
+    @ApiModelProperty(value = "An ID to uniquely identify this object.", readOnly = true)
     public String getIdentifier() {
         return identifier;
     }
@@ -52,7 +52,7 @@
         this.identifier = identifier;
     }
 
-    @ApiModelProperty("The name of the bucket.")
+    @ApiModelProperty(value = "The name of the bucket.", required = true)
     public String getName() {
         return name;
     }
@@ -61,7 +61,7 @@
         this.name = name;
     }
 
-    @ApiModelProperty("The timestamp of when the bucket was first created. This is set by the server at creation time.")
+    @ApiModelProperty(value = "The timestamp of when the bucket was first created. This is set by the server at creation time.", readOnly = true)
     public long getCreatedTimestamp() {
         return createdTimestamp;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
index 745bf81..ce9faa7 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
@@ -18,8 +18,8 @@
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import org.apache.nifi.registry.link.LinkableEntity;
 import org.apache.nifi.registry.authorization.Permissions;
+import org.apache.nifi.registry.link.LinkableEntity;
 
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotBlank;
@@ -58,7 +58,7 @@
         this.type = type;
     }
 
-    @ApiModelProperty("An ID to uniquely identify this object.")
+    @ApiModelProperty(value = "An ID to uniquely identify this object.", readOnly = true)
     public String getIdentifier() {
         return identifier;
     }
@@ -67,7 +67,7 @@
         this.identifier = identifier;
     }
 
-    @ApiModelProperty("The name of the item.")
+    @ApiModelProperty(value = "The name of the item.", required = true)
     public String getName() {
         return name;
     }
@@ -85,7 +85,7 @@
         this.description = description;
     }
 
-    @ApiModelProperty("The identifier of the bucket this items belongs to.")
+    @ApiModelProperty(value = "The identifier of the bucket this items belongs to. This cannot be changed after the item is created.", required = true)
     public String getBucketIdentifier() {
         return bucketIdentifier;
     }
@@ -103,7 +103,7 @@
         this.bucketName = bucketName;
     }
 
-    @ApiModelProperty("The timestamp of when the item was created.")
+    @ApiModelProperty(value = "The timestamp of when the item was created, as milliseconds since epoch.", readOnly = true)
     public long getCreatedTimestamp() {
         return createdTimestamp;
     }
@@ -112,7 +112,7 @@
         this.createdTimestamp = createdTimestamp;
     }
 
-    @ApiModelProperty("The timestamp of when the item was last modified.")
+    @ApiModelProperty(value = "The timestamp of when the item was last modified, as milliseconds since epoch.", readOnly = true)
     public long getModifiedTimestamp() {
         return modifiedTimestamp;
     }
@@ -121,7 +121,7 @@
         this.modifiedTimestamp = modifiedTimestamp;
     }
 
-    @ApiModelProperty("The type of item.")
+    @ApiModelProperty(value = "The type of item.", required = true)
     public BucketItemType getType() {
         return type;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedConnection.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedConnection.java
index 59740e9..4e9fd8e 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedConnection.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedConnection.java
@@ -71,7 +71,9 @@
         this.labelIndex = labelIndex;
     }
 
-    @ApiModelProperty("The z index of the connection.")
+    @ApiModelProperty(
+            value = "The z index of the connection.",
+            name = "zIndex")  // Jackson maps this method name to JSON key "zIndex", but Swagger does not by default
     public Long getzIndex() {
         return zIndex;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshot.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshot.java
index 5384923..bc82397 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshot.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshot.java
@@ -54,7 +54,7 @@
     private Bucket bucket;
 
 
-    @ApiModelProperty("The metadata for this snapshot")
+    @ApiModelProperty(value = "The metadata for this snapshot", required = true)
     public VersionedFlowSnapshotMetadata getSnapshotMetadata() {
         return snapshotMetadata;
     }
@@ -63,7 +63,7 @@
         this.snapshotMetadata = snapshotMetadata;
     }
 
-    @ApiModelProperty("The contents of the versioned flow")
+    @ApiModelProperty(value = "The contents of the versioned flow", required = true)
     public VersionedProcessGroup getFlowContents() {
         return flowContents;
     }
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshotMetadata.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshotMetadata.java
index 87e9817..2007279 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshotMetadata.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlowSnapshotMetadata.java
@@ -49,7 +49,7 @@
     private String comments;
 
 
-    @ApiModelProperty("The identifier of the bucket this snapshot belongs to.")
+    @ApiModelProperty(value = "The identifier of the bucket this snapshot belongs to.", required = true)
     public String getBucketIdentifier() {
         return bucketIdentifier;
     }
@@ -58,7 +58,7 @@
         this.bucketIdentifier = bucketIdentifier;
     }
 
-    @ApiModelProperty("The identifier of the flow this snapshot belongs to.")
+    @ApiModelProperty(value = "The identifier of the flow this snapshot belongs to.", required = true)
     public String getFlowIdentifier() {
         return flowIdentifier;
     }
@@ -67,7 +67,7 @@
         this.flowIdentifier = flowIdentifier;
     }
 
-    @ApiModelProperty("The version of this snapshot of the flow.")
+    @ApiModelProperty(value = "The version of this snapshot of the flow.", required = true)
     public int getVersion() {
         return version;
     }
@@ -76,7 +76,7 @@
         this.version = version;
     }
 
-    @ApiModelProperty("The timestamp when the flow was saved.")
+    @ApiModelProperty(value = "The timestamp when the flow was saved, as milliseconds since epoch.", readOnly = true)
     public long getTimestamp() {
         return timestamp;
     }
@@ -85,7 +85,7 @@
         this.timestamp = timestamp;
     }
 
-    @ApiModelProperty("The user that created this snapshot of the flow.")
+    @ApiModelProperty(value = "The user that created this snapshot of the flow.", readOnly = true)
     public String getAuthor() {
         return author;
     }
diff --git a/nifi-registry-web-api/pom.xml b/nifi-registry-web-api/pom.xml
index 746fc8b..797f31e 100644
--- a/nifi-registry-web-api/pom.xml
+++ b/nifi-registry-web-api/pom.xml
@@ -46,7 +46,7 @@
             <plugin>
                 <groupId>com.github.kongchen</groupId>
                 <artifactId>swagger-maven-plugin</artifactId>
-                <version>3.1.5</version>
+                <version>3.1.6</version>
                 <executions>
                     <execution>
                         <phase>compile</phase>
@@ -68,9 +68,7 @@
                                         <title>NiFi Registry REST API</title>
                                         <version>${project.version}</version>
                                         <description>
-                                            The Rest Api provides an interface to a registry with operations for saving,
-                                            versioning, reading NiFi flows
-                                            and components.
+                                            The REST API provides an interface to a registry with operations for saving, versioning, reading NiFi flows and components.
                                         </description>
                                         <contact>
                                             <name>Apache NiFi Registry</name>
@@ -82,6 +80,11 @@
                                             <name>Apache 2.0 License</name>
                                         </license>
                                     </info>
+                                    <securityDefinitions>
+                                        <securityDefinition>
+                                            <jsonPath>${project.basedir}/src/main/resources/swagger/security-definitions.json</jsonPath>
+                                        </securityDefinition>
+                                    </securityDefinitions>
                                     <templatePath>classpath:/templates/index.html.hbs</templatePath>
                                     <outputPath>
                                         ${project.build.directory}/${project.artifactId}-${project.version}/docs/rest-api/index.html
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
index 68bc493..9950bb3 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
@@ -21,6 +21,11 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import org.apache.nifi.registry.authorization.AccessPolicy;
+import org.apache.nifi.registry.authorization.AccessPolicySummary;
 import org.apache.nifi.registry.authorization.Resource;
 import org.apache.nifi.registry.exception.ResourceNotFoundException;
 import org.apache.nifi.registry.security.authorization.Authorizer;
@@ -28,8 +33,6 @@
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.authorization.AccessPolicy;
-import org.apache.nifi.registry.authorization.AccessPolicySummary;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,8 +61,9 @@
 @Component
 @Path("/policies")
 @Api(
-        value = "/policies",
-        description = "Endpoint for managing access policies."
+        value = "policies",
+        description = "Endpoint for managing access policies.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class AccessPolicyResource extends AuthorizableApplicationResource {
 
@@ -84,7 +88,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Creates an access policy",
-            response = AccessPolicy.class
+            response = AccessPolicy.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -127,7 +136,12 @@
     @ApiOperation(
             value = "Gets all access policies",
             response = AccessPolicy.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -158,7 +172,12 @@
     @Path("{id}")
     @ApiOperation(
             value = "Gets an access policy",
-            response = AccessPolicy.class
+            response = AccessPolicy.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -196,12 +215,12 @@
     @Path("{action}/{resource: .+}")
     @ApiOperation(
             value = "Gets an access policy for the specified action and resource",
-            notes = "Will return the effective policy if no specific policy exists for the specified action and resource. "
-                    + "Must have Read permissions to the policy with the desired action and resource. Permissions for the policy that is "
-                    + "returned will be indicated in the response. If the client does not have permissions to that policy, the response "
-                    + "will not include the policy and the permissions in the response will be marked accordingly. If the client does "
-                    + "not have permissions to the policy of the desired action and resource a 403 response will be returned.",
-            response = AccessPolicy.class
+            response = AccessPolicy.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -246,7 +265,12 @@
     @Path("{id}")
     @ApiOperation(
             value = "Updates a access policy",
-            response = AccessPolicy.class
+            response = AccessPolicy.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -294,7 +318,12 @@
     @Path("{id}")
     @ApiOperation(
             value = "Deletes an access policy",
-            response = AccessPolicy.class
+            response = AccessPolicy.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "delete"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -330,7 +359,12 @@
     @ApiOperation(
             value = "Gets the available resources that support access/authorization policies",
             response = Resource.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/policies") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
index 1bff31f..0804295 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
@@ -21,6 +21,7 @@
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.authorization.CurrentUser;
 import org.apache.nifi.registry.exception.AdministrationException;
@@ -64,7 +65,7 @@
 @Component
 @Path("/access")
 @Api(
-        value = "/access",
+        value = "access",
         description = "Endpoints for obtaining an access token or checking access status."
 )
 public class AccessResource extends ApplicationResource {
@@ -105,7 +106,8 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Returns the current client's authenticated identity and permissions to top-level resources",
-            response = CurrentUser.class
+            response = CurrentUser.class,
+            authorizations = {@Authorization(value = "Authorization")}
     )
     @ApiResponses({
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might be running unsecured.") })
@@ -205,14 +207,15 @@
                     "The token returned is formatted as a JSON Web Token (JWT). The token is base64 encoded and comprised of three parts. The header, " +
                     "the body, and the signature. The expiration of the token is a contained within the body. The token can be used in the Authorization header " +
                     "in the format 'Authorization: Bearer <token>'.",
-            response = String.class
+            response = String.class,
+            authorizations = { @Authorization("BasicAuth") }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry may not be configured to support login with username/password."),
             @ApiResponse(code = 500, message = HttpStatusMessages.MESSAGE_500) })
-    public Response createAccessTokenUsingFormLogin(@Context HttpServletRequest httpServletRequest) {
+    public Response createAccessTokenUsingBasicAuthCredentials(@Context HttpServletRequest httpServletRequest) {
 
         // only support access tokens when communicating over HTTPS
         if (!httpServletRequest.isSecure()) {
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
index 6c4c3ae..ed5c281 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
@@ -42,7 +42,7 @@
     public static final String FORWARDED_PORT_HTTP_HEADER = "X-Forwarded-Port";
     public static final String FORWARDED_CONTEXT_HTTP_HEADER = "X-Forwarded-Context";
 
-    protected static final String NON_GUARANTEED_ENDPOINT = "Note: This endpoint is subject to change as NiFi and its REST API evolve.";
+    protected static final String NON_GUARANTEED_ENDPOINT = "Note: This endpoint is subject to change as NiFi Registry and its REST API evolve.";
 
     private static final Logger logger = LoggerFactory.getLogger(ApplicationResource.class);
 
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
index 1d75104..d0be42c 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
@@ -16,13 +16,13 @@
  */
 package org.apache.nifi.registry.web.api;
 
+import org.apache.nifi.registry.authorization.Resource;
+import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
 import org.apache.nifi.registry.security.authorization.resource.ResourceType;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.bucket.BucketItem;
-import org.apache.nifi.registry.authorization.Resource;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
index e645a40..788f318 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
@@ -21,11 +21,14 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
 import org.apache.nifi.registry.exception.ResourceNotFoundException;
 import org.apache.nifi.registry.flow.VersionedFlow;
-import org.apache.nifi.registry.diff.VersionedFlowDifference;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
 import org.apache.nifi.registry.security.authorization.Authorizer;
@@ -58,8 +61,9 @@
 @Component
 @Path("/buckets/{bucketId}/flows")
 @Api(
-        value = "bucket >> flows",
-        description = "Create flows scoped to an existing bucket in the registry."
+        value = "bucket_flows",
+        description = "Create flows scoped to an existing bucket in the registry.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class BucketFlowResource extends AuthorizableApplicationResource {
 
@@ -88,7 +92,12 @@
     @ApiOperation(
             value = "Creates a flow",
             notes = "The flow id is created by the server and populated in the returned entity.",
-            response = VersionedFlow.class
+            response = VersionedFlow.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -100,7 +109,7 @@
             @PathParam("bucketId")
             @ApiParam("The bucket identifier")
             final String bucketId,
-            @ApiParam("The details of the flow to create.")
+            @ApiParam(value = "The details of the flow to create.", required = true)
             final VersionedFlow flow) {
 
         authorizeBucketAccess(RequestAction.WRITE, bucketId);
@@ -117,7 +126,12 @@
     @ApiOperation(
             value = "Gets all flows in the given bucket",
             response = VersionedFlow.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -145,7 +159,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Gets a flow",
-            response = VersionedFlow.class
+            response = VersionedFlow.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -176,7 +195,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Updates a flow",
-            response = VersionedFlow.class
+            response = VersionedFlow.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -191,14 +215,15 @@
             @PathParam("flowId")
             @ApiParam("The flow identifier")
                 final String flowId,
-            @ApiParam("The updated flow")
+            @ApiParam(value = "The updated flow", required = true)
                 final VersionedFlow flow) {
 
         verifyPathParamsMatchBody(bucketId, flowId, flow);
-        setBucketItemMetadataIfMissing(bucketId, flowId, flow);
-
         authorizeBucketAccess(RequestAction.WRITE, bucketId);
 
+        // bucketId and flowId fields are optional in the body parameter, but required before calling the service layer
+        setBucketItemMetadataIfMissing(bucketId, flowId, flow);
+
         final VersionedFlow updatedFlow = registryService.updateFlow(flow);
         permissionsService.populateItemPermissions(updatedFlow);
         linkService.populateFlowLinks(updatedFlow);
@@ -211,8 +236,13 @@
     @Consumes(MediaType.WILDCARD)
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
-            value = "Deletes a flow.",
-            response = VersionedFlow.class
+            value = "Deletes a flow, including all saved versions of that flow.",
+            response = VersionedFlow.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "delete"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -238,8 +268,14 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Creates the next version of a flow",
-            notes = "The version number is created by the server and populated in the returned entity.",
-            response = VersionedFlowSnapshot.class
+            notes = "The version number of the object being created must be the next available version integer. " +
+                    "Flow versions are immutable after they are created.",
+            response = VersionedFlowSnapshot.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -252,14 +288,15 @@
             @ApiParam("The bucket identifier")
                 final String bucketId,
             @PathParam("flowId")
-            @ApiParam("The flow identifier")
+            @ApiParam(value = "The flow identifier")
                 final String flowId,
-            @ApiParam("The new versioned flow snapshot.")
+            @ApiParam(value = "The new versioned flow snapshot.", required = true)
                 final VersionedFlowSnapshot snapshot) {
 
         verifyPathParamsMatchBody(bucketId, flowId, snapshot);
         authorizeBucketAccess(RequestAction.WRITE, bucketId);
 
+        // bucketId and flowId fields are optional in the body parameter, but required before calling the service layer
         setSnaphotMetadataIfMissing(bucketId, flowId, snapshot);
 
         final String userIdentity = NiFiUserUtils.getNiFiUserIdentity();
@@ -283,7 +320,12 @@
     @ApiOperation(
             value = "Gets summary information for all versions of a flow. Versions are ordered newest->oldest.",
             response = VersionedFlowSnapshotMetadata.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -314,7 +356,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Get the latest version of a flow",
-            response = VersionedFlowSnapshot.class
+            response = VersionedFlowSnapshot.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -350,7 +397,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Get the metadata for the latest version of a flow",
-            response = VersionedFlowSnapshotMetadata.class
+            response = VersionedFlowSnapshotMetadata.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -385,7 +437,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Gets the given version of a flow",
-            response = VersionedFlowSnapshot.class
+            response = VersionedFlowSnapshot.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -417,7 +474,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Returns a list of differences between 2 versions of a flow",
-            response = VersionedFlowDifference.class
+            response = VersionedFlowDifference.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -425,14 +487,20 @@
             @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403),
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409)})
-    public Response getFlowDiff(@PathParam("bucketId")
-                                @ApiParam("The bucket identifier") final String bucketId,
-                                @PathParam("flowId")
-                                @ApiParam("The flow identifier") final String flowId,
-                                @PathParam("versionA")
-                                @ApiParam("The first version number") final Integer versionNumberA,
-                                @PathParam("versionB")
-                                @ApiParam("The second version number") final Integer versionNumberB) {
+    public Response getFlowDiff(
+            @PathParam("bucketId")
+            @ApiParam("The bucket identifier")
+            final String bucketId,
+            @PathParam("flowId")
+            @ApiParam("The flow identifier")
+            final String flowId,
+            @PathParam("versionA")
+            @ApiParam("The first version number")
+            final Integer versionNumberA,
+            @PathParam("versionB")
+            @ApiParam("The second version number")
+            final Integer versionNumberB) {
+        authorizeBucketAccess(RequestAction.READ, bucketId);
         VersionedFlowDifference result = registryService.getFlowDiff(bucketId, flowId, versionNumberA, versionNumberB);
         return Response.status(Response.Status.OK).entity(result).build();
     }
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
index 1034539..f94117d 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
@@ -21,6 +21,9 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.bucket.BucketItem;
@@ -59,9 +62,10 @@
 @Component
 @Path("/buckets")
 @Api(
-        value = "/buckets",
-        description = "Create named buckets in the registry to store NiFI objects such flows and extensions. " +
-                "Search for and retrieve existing buckets."
+        value = "buckets",
+        description = "Create named buckets in the registry to store NiFi objects such flows and extensions. " +
+                "Search for and retrieve existing buckets.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class BucketResource extends AuthorizableApplicationResource {
 
@@ -94,14 +98,19 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Creates a bucket",
-            response = Bucket.class
+            response = Bucket.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/buckets") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
             @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403) })
     public Response createBucket(
-            @ApiParam("The bucket to create")
+            @ApiParam(value = "The bucket to create", required = true)
             final Bucket bucket) {
         authorizeAccess(RequestAction.WRITE);
         final Bucket createdBucket = registryService.createBucket(bucket);
@@ -151,7 +160,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Gets a bucket",
-            response = Bucket.class
+            response = Bucket.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@@ -176,7 +190,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Updates a bucket",
-            response = Bucket.class
+            response = Bucket.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -188,7 +207,7 @@
             @PathParam("bucketId")
             @ApiParam("The bucket identifier")
             final String bucketId,
-            @ApiParam("The updated bucket")
+            @ApiParam(value = "The updated bucket", required = true)
             final Bucket bucket) {
 
         if (StringUtils.isBlank(bucketId)) {
@@ -219,7 +238,12 @@
     @Produces(MediaType.APPLICATION_JSON)
     @ApiOperation(
             value = "Deletes a bucket along with all objects stored in the bucket",
-            response = Bucket.class
+            response = Bucket.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "delete"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
index ce11b0d..4ea059e 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
@@ -18,8 +18,9 @@
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.apache.nifi.registry.security.authorization.Authorizer;
+import io.swagger.annotations.Authorization;
 import org.apache.nifi.registry.field.Fields;
+import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.apache.nifi.registry.service.RegistryService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -36,8 +37,9 @@
 @Component
 @Path("/flows")
 @Api(
-        value = "/flows",
-        description = "Gets metadata about flows."
+        value = "flows",
+        description = "Gets metadata about flows.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class FlowResource extends AuthorizableApplicationResource {
 
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
index 315b442..645f34d 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
@@ -21,6 +21,9 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
 import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.field.Fields;
 import org.apache.nifi.registry.security.authorization.Authorizer;
@@ -51,8 +54,9 @@
 @Component
 @Path("/items")
 @Api(
-        value = "/items",
-        description = "Retrieve items across all buckets for which the user is authorized."
+        value = "items",
+        description = "Retrieve items across all buckets for which the user is authorized.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class ItemResource extends AuthorizableApplicationResource {
 
@@ -124,7 +128,12 @@
             value = "Gets items of the given bucket",
             response = BucketItem.class,
             responseContainer = "List",
-            nickname = "getItemsInBucket"
+            nickname = "getItemsInBucket",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
index 1d838c4..2fc2e97 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
@@ -21,15 +21,18 @@
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.registry.authorization.UserGroup;
 import org.apache.nifi.registry.exception.ResourceNotFoundException;
 import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.authorization.User;
-import org.apache.nifi.registry.authorization.UserGroup;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,7 +61,8 @@
 @Path("tenants")
 @Api(
         value = "tenants",
-        description = "Endpoint for managing users and user groups."
+        description = "Endpoint for managing users and user groups.",
+        authorizations = { @Authorization("Authorization") }
 )
 public class TenantResource extends AuthorizableApplicationResource {
 
@@ -88,7 +92,12 @@
     @ApiOperation(
             value = "Creates a user",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = User.class
+            response = User.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -97,7 +106,8 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response createUser(
-            @Context final HttpServletRequest httpServletRequest,
+            @Context
+            final HttpServletRequest httpServletRequest,
             @ApiParam(value = "The user configuration details.", required = true)
             final User requestUser) {
 
@@ -134,7 +144,12 @@
             value = "Gets all users",
             notes = NON_GUARANTEED_ENDPOINT,
             response = User.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -166,7 +181,12 @@
     @ApiOperation(
             value = "Gets a user",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = User.class
+            response = User.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -204,7 +224,12 @@
     @ApiOperation(
             value = "Updates a user",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = User.class
+            response = User.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -213,9 +238,11 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response updateUser(
-            @Context final HttpServletRequest httpServletRequest,
+            @Context
+            final HttpServletRequest httpServletRequest,
             @ApiParam(value = "The user id.", required = true)
-            @PathParam("id") final String identifier,
+            @PathParam("id")
+            final String identifier,
             @ApiParam(value = "The user configuration details.", required = true)
             final User requestUser) {
 
@@ -254,7 +281,12 @@
     @ApiOperation(
             value = "Deletes a user",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = User.class
+            response = User.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "delete"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -263,9 +295,11 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response removeUser(
-            @Context final HttpServletRequest httpServletRequest,
+            @Context
+            final HttpServletRequest httpServletRequest,
             @ApiParam(value = "The user id.", required = true)
-            @PathParam("id") final String identifier) {
+            @PathParam("id")
+            final String identifier) {
 
         verifyAuthorizerSupportsConfigurableUserGroups();
         authorizeAccess(RequestAction.DELETE);
@@ -296,7 +330,12 @@
     @ApiOperation(
             value = "Creates a user group",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = UserGroup.class
+            response = UserGroup.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -305,11 +344,10 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response createUserGroup(
-            @Context final HttpServletRequest httpServletRequest,
-            @ApiParam(
-                    value = "The user group configuration details.",
-                    required = true
-            ) final UserGroup requestUserGroup) {
+            @Context
+            final HttpServletRequest httpServletRequest,
+            @ApiParam(value = "The user group configuration details.", required = true)
+            final UserGroup requestUserGroup) {
 
         verifyAuthorizerSupportsConfigurableUserGroups();
         authorizeAccess(RequestAction.WRITE);
@@ -343,7 +381,12 @@
             value = "Gets all user groups",
             notes = NON_GUARANTEED_ENDPOINT,
             response = UserGroup.class,
-            responseContainer = "List"
+            responseContainer = "List",
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -372,7 +415,12 @@
     @ApiOperation(
             value = "Gets a user group",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = UserGroup.class
+            response = UserGroup.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "read"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -411,7 +459,12 @@
     @ApiOperation(
             value = "Updates a user group",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = UserGroup.class
+            response = UserGroup.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "write"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -420,9 +473,11 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response updateUserGroup(
-            @Context final HttpServletRequest httpServletRequest,
+            @Context
+            final HttpServletRequest httpServletRequest,
             @ApiParam(value = "The user group id.", required = true)
-            @PathParam("id") final String identifier,
+            @PathParam("id")
+            final String identifier,
             @ApiParam(value = "The user group configuration details.", required = true)
             final UserGroup requestUserGroup) {
 
@@ -462,7 +517,12 @@
     @ApiOperation(
             value = "Deletes a user group",
             notes = NON_GUARANTEED_ENDPOINT,
-            response = UserGroup.class
+            response = UserGroup.class,
+            extensions = {
+                    @Extension(name = "access-policy", properties = {
+                            @ExtensionProperty(name = "action", value = "delete"),
+                            @ExtensionProperty(name = "resource", value = "/tenants") })
+            }
     )
     @ApiResponses({
             @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
@@ -471,7 +531,8 @@
             @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
             @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
     public Response removeUserGroup(
-            @Context final HttpServletRequest httpServletRequest,
+            @Context
+            final HttpServletRequest httpServletRequest,
             @ApiParam(value = "The user group id.", required = true)
             @PathParam("id")
             final String identifier) {
diff --git a/nifi-registry-web-api/src/main/resources/swagger/security-definitions.json b/nifi-registry-web-api/src/main/resources/swagger/security-definitions.json
new file mode 100644
index 0000000..411fb3b
--- /dev/null
+++ b/nifi-registry-web-api/src/main/resources/swagger/security-definitions.json
@@ -0,0 +1,12 @@
+{
+  "BasicAuth": {
+    "type": "basic",
+    "description": "HTTP Basic Auth"
+  },
+  "Authorization": {
+    "type": "apiKey",
+    "name": "Authorization",
+    "in": "header",
+    "description": "NiFi Registry Auth Token (JWT)"
+  }
+}
diff --git a/nifi-registry-web-api/src/main/resources/templates/operation.hbs b/nifi-registry-web-api/src/main/resources/templates/operation.hbs
index 5fb25e7..64bd582 100644
--- a/nifi-registry-web-api/src/main/resources/templates/operation.hbs
+++ b/nifi-registry-web-api/src/main/resources/templates/operation.hbs
@@ -83,7 +83,15 @@
                 <td>{{@key}}</td>
                 <td>
                     {{#if schema}}
-                        {{#schema.$ref}}<a class="type-link" href="javascript:void(0);">{{basename schema.$ref}}</a>{{/schema.$ref}}
+                        {{#ifeq schema.type "array"}}
+                            {{#if schema.items.$ref}}
+                                array[<a class="type-link" href="javascript:void(0);">{{basename schema.items.$ref}}</a>]
+                            {{else}}
+                                array[{{schema.items.type}}]
+                            {{/if}}
+                        {{else}}
+                            {{#schema.$ref}}<a class="type-link" href="javascript:void(0);">{{basename schema.$ref}}</a>{{/schema.$ref}}
+                        {{/ifeq}}
                     {{else}}
                         string
                     {{/if}}
@@ -93,12 +101,10 @@
             {{/each}}
         </tbody>
     </table>
-    <div class="title">Authorization</div>
-    <div class="authorization details">
-        {{#security}}
-            {{#each this}}
-            <div>{{@key}}</div>
-            {{/each}}
-        {{/security}}
-    </div>
+    {{#if vendorExtensions.x-access-policy}}
+        <div class="title">Authorization</div>
+        <div class="authorization details">
+            Requires access policy: {{vendorExtensions.x-access-policy.action}}:{{vendorExtensions.x-access-policy.resource}}
+        </div>
+    {{/if}}
 </div>
\ No newline at end of file
diff --git a/nifi-registry-web-api/src/test/resources/keys/README.md b/nifi-registry-web-api/src/test/resources/keys/README.md
index c49bc18..c3059cf 100644
--- a/nifi-registry-web-api/src/test/resources/keys/README.md
+++ b/nifi-registry-web-api/src/test/resources/keys/README.md
@@ -37,6 +37,7 @@
 
 
 You should now have a directory with the following contents:
+
     keys/
      +-- client-ks.jks      # client keystore: keystorePass=clientKeystorePassword, keyPass=u1Pass
      +-- localhost-ks.jks   # server keystore: keystorePass=localhostKeystorePassword, keyPass=localhostKeystorePassword
diff --git a/pom.xml b/pom.xml
index d3e8fbb..2a0634c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -695,7 +695,9 @@
                         <exclude>.github/PULL_REQUEST_TEMPLATE.md
                         </exclude> <!-- PR Template for GitHub that does not have a mechanism of including comments -->
                         <exclude>**/resources/banner.txt
-                        </exclude><!-- Console banner text that does not have a mechanism of including comments -->
+                        </exclude> <!-- Console banner text that does not have a mechanism of including comments -->
+                        <exclude>src/main/resources/swagger/security-definitions.json
+                        </exclude> <!-- A maven-swagger-plugin input file that does not have a mechanism of including comments -->
                     </excludes>
                 </configuration>
                 <dependencies>