You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2017/11/08 16:14:00 UTC

nifi-registry git commit: NIFIREG-46 Add authorizedActions field to Bucket API model object

Repository: nifi-registry
Updated Branches:
  refs/heads/master 90f36dd22 -> 9b41af80b


NIFIREG-46 Add authorizedActions field to Bucket API model object

This closes #30.

Signed-off-by: Bryan Bende <bb...@apache.org>


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

Branch: refs/heads/master
Commit: 9b41af80b855d82ffa4163c3d1e074d45376437f
Parents: 90f36dd
Author: Kevin Doran <kd...@gmail.com>
Authored: Wed Nov 1 15:02:19 2017 -0400
Committer: Bryan Bende <bb...@apache.org>
Committed: Wed Nov 8 11:13:45 2017 -0500

----------------------------------------------------------------------
 .../org/apache/nifi/registry/bucket/Bucket.java |  24 ++-
 .../registry/model/authorization/Tenant.java    |  27 +++
 .../nifi/registry/model/authorization/User.java |  22 ---
 .../registry/model/authorization/UserGroup.java |  23 ---
 .../registry/service/AuthorizationService.java  | 122 ++++++-------
 .../nifi/registry/web/api/AccessResource.java   | 106 ++---------
 .../api/AuthorizableApplicationResource.java    |   4 +-
 .../nifi/registry/web/api/BucketResource.java   |  71 ++++++--
 .../nifi/registry/web/api/ItemResource.java     |  10 +-
 .../nifi/registry/web/api/ResourceResource.java |  15 +-
 .../web/mapper/NiFiRegistryJsonProvider.java    |  48 +++++
 .../web/mapper/ObjectMapperContextResolver.java |  44 -----
 .../security/NiFiRegistrySecurityConfig.java    |   6 +-
 .../apache/nifi/registry/web/api/BucketsIT.java |   8 +-
 .../registry/web/api/IntegrationTestBase.java   |  29 ++-
 .../nifi/registry/web/api/SecureLdapIT.java     | 175 +++++++++++++++++--
 16 files changed, 448 insertions(+), 286 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
----------------------------------------------------------------------
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 314598e..3ba9b22 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,13 +18,14 @@ package org.apache.nifi.registry.bucket;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
-import org.apache.nifi.registry.link.LinkableEntity;
 import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.link.LinkableEntity;
 
 import javax.validation.Valid;
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotBlank;
 import javax.xml.bind.annotation.XmlRootElement;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 
@@ -43,10 +44,11 @@ public class Bucket extends LinkableEntity {
 
     private String description;
 
+    private Set<String> authorizedActions;
+
     @Valid
     private Set<VersionedFlow> versionedFlows;
 
-
     @ApiModelProperty("An ID to uniquely identify this object.")
     public String getIdentifier() {
         return identifier;
@@ -83,6 +85,24 @@ public class Bucket extends LinkableEntity {
         this.description = description;
     }
 
+    @ApiModelProperty("A list of actions the client is authorized to perform for this bucket.")
+    public Set<String> getAuthorizedActions() {
+        return authorizedActions;
+    }
+
+    public void setAuthorizedActions(Set<String> authorizedActions) {
+        this.authorizedActions = authorizedActions;
+    }
+
+    public void addAuthorizedAction(String action) {
+        if (action != null) {
+            if (this.authorizedActions == null) {
+                this.authorizedActions = new HashSet<>();
+            }
+            authorizedActions.add(action);
+        }
+    }
+
     @ApiModelProperty(value = "The versioned flows in the bucket.", readOnly = true)
     public Set<VersionedFlow> getVersionedFlows() {
         return versionedFlows;

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
index 662e3bf..87f1242 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Tenant.java
@@ -19,6 +19,10 @@ package org.apache.nifi.registry.model.authorization;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * A tenant of this NiFi Registry
  */
@@ -27,6 +31,7 @@ public class Tenant {
 
     private String identifier;
     private String identity;
+    private Set<AccessPolicySummary> accessPolicies;
 
     public Tenant() {}
 
@@ -58,4 +63,26 @@ public class Tenant {
     public void setIdentity(String identity) {
         this.identity = identity;
     }
+
+    @ApiModelProperty(
+            value = "The access policies granted to this tenant. This field is read only",
+            readOnly = true
+    )
+    public Set<AccessPolicySummary> getAccessPolicies() {
+        return accessPolicies;
+    }
+
+    public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
+        this.accessPolicies = accessPolicies;
+    }
+
+    public void addAccessPolicies(Collection<AccessPolicySummary> accessPolicies) {
+        if (accessPolicies != null) {
+            if (this.accessPolicies == null) {
+                this.accessPolicies = new HashSet<>();
+            }
+            this.accessPolicies.addAll(accessPolicies);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
index 76ed867..449a274 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/User.java
@@ -27,7 +27,6 @@ import java.util.Set;
 public class User extends Tenant {
 
     private Set<Tenant> userGroups;
-    private Set<AccessPolicySummary> accessPolicies;
 
     public User() {}
 
@@ -56,25 +55,4 @@ public class User extends Tenant {
         }
     }
 
-    @ApiModelProperty(
-            value = "The access policies granted to this user. This field is read only",
-            readOnly = true
-    )
-    public Set<AccessPolicySummary> getAccessPolicies() {
-        return accessPolicies;
-    }
-
-    public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
-        this.accessPolicies = accessPolicies;
-    }
-
-    public void addAccessPolicies(Collection<? extends AccessPolicySummary> accessPolicies) {
-        if (accessPolicies != null) {
-            if (this.accessPolicies == null) {
-                this.accessPolicies = new HashSet<>();
-            }
-            this.accessPolicies.addAll(accessPolicies);
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
index 51b1297..4b14a20 100644
--- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/UserGroup.java
@@ -59,27 +59,4 @@ public class UserGroup extends Tenant {
         }
     }
 
-    /**
-     * @return The access policies set for this user group
-     */
-    @ApiModelProperty(
-            value = "The access policies granted to this user group.",
-            readOnly = true
-    )
-    public Set<AccessPolicySummary> getAccessPolicies() {
-        return accessPolicies;
-    }
-
-    public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
-        this.accessPolicies = accessPolicies;
-    }
-
-    public void addAccessPolicies(Collection<? extends AccessPolicySummary> accessPolicies) {
-        if (accessPolicies != null) {
-            if (this.accessPolicies == null) {
-                this.accessPolicies = new HashSet<>();
-            }
-            this.accessPolicies.addAll(accessPolicies);
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 2567b55..ead73cd 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -16,6 +16,13 @@
  */
 package org.apache.nifi.registry.service;
 
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.model.authorization.AccessPolicy;
+import org.apache.nifi.registry.model.authorization.AccessPolicySummary;
+import org.apache.nifi.registry.model.authorization.Resource;
+import org.apache.nifi.registry.model.authorization.Tenant;
+import org.apache.nifi.registry.model.authorization.User;
+import org.apache.nifi.registry.model.authorization.UserGroup;
 import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
 import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext;
 import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
@@ -38,13 +45,6 @@ import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestr
 import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
 import org.apache.nifi.registry.security.authorization.resource.ResourceType;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.bucket.Bucket;
-import org.apache.nifi.registry.model.authorization.AccessPolicy;
-import org.apache.nifi.registry.model.authorization.AccessPolicySummary;
-import org.apache.nifi.registry.model.authorization.Resource;
-import org.apache.nifi.registry.model.authorization.Tenant;
-import org.apache.nifi.registry.model.authorization.User;
-import org.apache.nifi.registry.model.authorization.UserGroup;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -150,6 +150,15 @@ public class AuthorizationService {
         }
     }
 
+    public User getUserByIdentity(String identity) {
+        this.readLock.lock();
+        try {
+            return userToDTO(userGroupProvider.getUserByIdentity(identity));
+        } finally {
+            this.readLock.unlock();
+        }
+    }
+
     public User updateUser(User user) {
         verifyUserGroupProviderIsConfigurable();
         this.writeLock.lock();
@@ -198,19 +207,6 @@ public class AuthorizationService {
         }
     }
 
-//    private List<UserGroup> getUserGroupsForUser(String userIdentifier) {
-//        this.readLock.lock();
-//        try {
-//            return userGroupProvider.getGroups()
-//                    .stream()
-//                    .filter(group -> group.getUsers().contains(userIdentifier))
-//                    .map(this::userGroupToDTO)
-//                    .collect(Collectors.toList());
-//        } finally {
-//            this.readLock.unlock();
-//        }
-//    }
-
     public UserGroup getUserGroup(String identifier) {
         this.readLock.lock();
         try {
@@ -295,18 +291,6 @@ public class AuthorizationService {
         }
     }
 
-    public List<AccessPolicy> getAccessPoliciesForUser(String userIdentifier) {
-        readLock.lock();
-        try {
-            return accessPolicyProvider.getAccessPolicies().stream()
-                    .filter(accessPolicy -> accessPolicy.getUsers().contains(userIdentifier))
-                    .map(this::accessPolicyToDTO)
-                    .collect(Collectors.toList());
-        } finally {
-            readLock.unlock();
-        }
-    }
-
     private List<AccessPolicySummary> getAccessPolicySummariesForUser(String userIdentifier) {
         readLock.lock();
         try {
@@ -364,26 +348,33 @@ public class AuthorizationService {
 
     // ---------------------- Resource Lookup methods --------------------------------------
 
+    public List<Resource> getResources() {
+        final List<Resource> dtoResources =
+                getAuthorizableResources()
+                        .stream()
+                        .map(AuthorizationService::resourceToDTO)
+                        .collect(Collectors.toList());
+        return dtoResources;
+    }
+
+    public List<Resource> getAuthorizedResources(RequestAction actionType) {
+        return getAuthorizedResources(actionType, null);
+    }
+
     public List<Resource> getAuthorizedResources(RequestAction actionType, ResourceType resourceType) {
         final List<Resource> authorizedResources =
-                getAllAuthorizableResources()
+                getAuthorizableResources(resourceType)
                         .stream()
                         .filter(resource -> {
                             String resourceId = resource.getIdentifier();
-                            if (resourceType != null) {
-                                if (!resourceId.startsWith(resourceType.getValue())) {
-                                    return false;
-                                }
-                            }
                             try {
                                 authorizableLookup
                                         .getAuthorizableByResource(resource.getIdentifier())
                                         .authorize(authorizer, actionType, NiFiUserUtils.getNiFiUser());
+                                return true;
                             } catch (AccessDeniedException e) {
                                 return false;
                             }
-                            return true;
-
                         })
                         .map(AuthorizationService::resourceToDTO)
                         .collect(Collectors.toList());
@@ -391,20 +382,6 @@ public class AuthorizationService {
         return authorizedResources;
     }
 
-    public List<Resource> getAuthorizedResources(RequestAction actionType) {
-        return getAuthorizedResources(actionType, null);
-    }
-
-    public List<Resource> getResources() {
-        final List<Resource> dtoResources =
-                getAllAuthorizableResources()
-                        .stream()
-                        .map(AuthorizationService::resourceToDTO)
-                        .collect(Collectors.toList());
-        return dtoResources;
-    }
-
-
     // ---------------------- Private Helper methods --------------------------------------
 
     private void verifyUserGroupProviderIsConfigurable() {
@@ -419,17 +396,32 @@ public class AuthorizationService {
         }
     }
 
-    private List<org.apache.nifi.registry.security.authorization.Resource> getAllAuthorizableResources() {
+    private List<org.apache.nifi.registry.security.authorization.Resource> getAuthorizableResources() {
+        return getAuthorizableResources(null);
+    }
+
+    private List<org.apache.nifi.registry.security.authorization.Resource> getAuthorizableResources(ResourceType includeFilter) {
+
         final List<org.apache.nifi.registry.security.authorization.Resource> resources = new ArrayList<>();
-        resources.add(ResourceFactory.getPoliciesResource());
-        resources.add(ResourceFactory.getTenantResource());
-        resources.add(ResourceFactory.getProxyResource());
-        resources.add(ResourceFactory.getResourceResource());
 
-        // add all buckets
-        resources.add(ResourceFactory.getBucketsResource());
-        for (final Bucket bucket : registryService.getBuckets()) {
-            resources.add(ResourceFactory.getChildResource(ResourceType.Bucket, bucket.getIdentifier(), bucket.getName()));
+        if (includeFilter == null || includeFilter.equals(ResourceType.Policy)) {
+            resources.add(ResourceFactory.getPoliciesResource());
+        }
+        if (includeFilter == null || includeFilter.equals(ResourceType.Tenant)) {
+            resources.add(ResourceFactory.getTenantResource());
+        }
+        if (includeFilter == null || includeFilter.equals(ResourceType.Proxy)) {
+            resources.add(ResourceFactory.getProxyResource());
+        }
+        if (includeFilter == null || includeFilter.equals(ResourceType.Resource)) {
+            resources.add(ResourceFactory.getResourceResource());
+        }
+        if (includeFilter == null || includeFilter.equals(ResourceType.Bucket)) {
+            resources.add(ResourceFactory.getBucketsResource());
+            // add all buckets
+            for (final Bucket bucket : registryService.getBuckets()) {
+                resources.add(ResourceFactory.getChildResource(ResourceType.Bucket, bucket.getIdentifier(), bucket.getName()));
+            }
         }
 
         return resources;
@@ -533,7 +525,7 @@ public class AuthorizationService {
     private static org.apache.nifi.registry.model.authorization.User userToDTO(
             final org.apache.nifi.registry.security.authorization.User user,
             final Collection<? extends Tenant> userGroups,
-            final Collection<? extends AccessPolicySummary> accessPolicies) {
+            final Collection<AccessPolicySummary> accessPolicies) {
 
         if (user == null) {
             return null;
@@ -562,7 +554,7 @@ public class AuthorizationService {
     private static org.apache.nifi.registry.model.authorization.UserGroup userGroupToDTO(
             final org.apache.nifi.registry.security.authorization.Group userGroup,
             final Collection<? extends Tenant> users,
-            final Collection<? extends AccessPolicySummary> accessPolicies) {
+            final Collection<AccessPolicySummary> accessPolicies) {
         if (userGroup == null) {
             return null;
         }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
----------------------------------------------------------------------
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 ad923c4..2732d5a 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
@@ -16,39 +16,26 @@
  */
 package org.apache.nifi.registry.web.api;
 
-import io.jsonwebtoken.JwtException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.exception.AdministrationException;
+import org.apache.nifi.registry.model.authorization.AccessStatus;
 import org.apache.nifi.registry.security.authentication.AuthenticationResponse;
 import org.apache.nifi.registry.security.authentication.LoginCredentials;
 import org.apache.nifi.registry.security.authentication.LoginIdentityProvider;
 import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException;
 import org.apache.nifi.registry.security.authentication.exception.InvalidLoginCredentialsException;
-import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
 import org.apache.nifi.registry.security.authorization.user.NiFiUser;
-import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails;
-import org.apache.nifi.registry.exception.AdministrationException;
-import org.apache.nifi.registry.model.authorization.AccessStatus;
-import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
-import org.apache.nifi.registry.web.security.authentication.ProxiedEntitiesUtils;
-import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException;
-import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationFilter;
-import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationProvider;
-import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationRequestToken;
+import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
+import org.apache.nifi.registry.service.AuthorizationService;
 import org.apache.nifi.registry.web.security.authentication.jwt.JwtService;
 import org.apache.nifi.registry.web.security.authentication.token.LoginAuthenticationToken;
-import org.apache.nifi.registry.web.security.authentication.token.NiFiAuthenticationToken;
-import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationProvider;
-import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationRequestToken;
-import org.apache.nifi.registry.web.security.authentication.x509.X509CertificateExtractor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
 import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;
@@ -62,7 +49,6 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.net.URI;
-import java.security.cert.X509Certificate;
 import java.util.concurrent.TimeUnit;
 
 @Component
@@ -75,27 +61,16 @@ public class AccessResource extends ApplicationResource {
 
     private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
 
-    private X509CertificateExtractor certificateExtractor;
-    private X509AuthenticationProvider x509AuthenticationProvider;
-    private X509PrincipalExtractor x509principalExtractor;
     private LoginIdentityProvider loginIdentityProvider;
-    private JwtAuthenticationProvider jwtAuthenticationProvider;
     private JwtService jwtService;
 
     @Autowired
-    public AccessResource(X509CertificateExtractor certificateExtractor,
-                          X509AuthenticationProvider x509AuthenticationProvider,
-                          X509PrincipalExtractor x509principalExtractor,
-                          LoginIdentityProvider loginIdentityProvider,
-                          JwtAuthenticationProvider jwtAuthenticationProvider,
-                          JwtService jwtService
-    ) {
-        this.certificateExtractor = certificateExtractor;
-        this.x509AuthenticationProvider = x509AuthenticationProvider;
-        this.x509principalExtractor = x509principalExtractor;
-        this.loginIdentityProvider = loginIdentityProvider;
-        this.jwtAuthenticationProvider = jwtAuthenticationProvider;
+    public AccessResource(
+            AuthorizationService authorizationService,
+            JwtService jwtService,
+            LoginIdentityProvider loginIdentityProvider) {
         this.jwtService = jwtService;
+        this.loginIdentityProvider = loginIdentityProvider;
     }
 
     /**
@@ -124,60 +99,15 @@ public class AccessResource extends ApplicationResource {
 
         final AccessStatus accessStatus = new AccessStatus();
 
-        try {
-            final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest);
-
-            // if there is not certificate, consider a token
-            if (certificates == null) {
-
-                // look for an authorization token
-                final String authorization = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION);
-
-                // if there is no authorization header, we don't know the user
-                if (authorization == null) {
-                    accessStatus.setStatus(AccessStatus.Status.UNKNOWN.name());
-                    accessStatus.setMessage("No credentials supplied, unknown user.");
-                } else {
-                    try {
-                        // Extract the Base64 encoded token from the Authorization header
-                        final String token = StringUtils.substringAfterLast(authorization, " ");
-
-                        final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token, httpServletRequest.getRemoteAddr());
-                        final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) jwtAuthenticationProvider.authenticate(jwtRequest);
-                        final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser();
-
-                        // set the user identity
-                        accessStatus.setIdentity(nifiUser.getIdentity());
-
-                        // attempt authorize to /flow
-                        accessStatus.setStatus(AccessStatus.Status.ACTIVE.name());
-                        accessStatus.setMessage("You are already logged in.");
-                    } catch (JwtException e) {
-                        throw new InvalidAuthenticationException(e.getMessage(), e);
-                    }
-                }
-            } else {
-                try {
-                    final X509AuthenticationRequestToken x509Request = new X509AuthenticationRequestToken(
-                            httpServletRequest.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), x509principalExtractor, certificates, httpServletRequest.getRemoteAddr());
-
-                    final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) x509AuthenticationProvider.authenticate(x509Request);
-                    final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser();
-
-                    // set the user identity
-                    accessStatus.setIdentity(nifiUser.getIdentity());
-
-                    // attempt authorize to /flow
-                    accessStatus.setStatus(AccessStatus.Status.ACTIVE.name());
-                    accessStatus.setMessage("You are already logged in.");
-                } catch (final IllegalArgumentException iae) {
-                    throw new InvalidAuthenticationException(iae.getMessage(), iae);
-                }
-            }
-        } catch (final UntrustedProxyException upe) {
-            throw new AccessDeniedException(upe.getMessage(), upe);
-        } catch (final AuthenticationServiceException ase) {
-            throw new AdministrationException(ase.getMessage(), ase);
+        NiFiUser currentUser = NiFiUserUtils.getNiFiUser();
+        if (currentUser == null || currentUser.getIdentity() == null || currentUser.isAnonymous()) {
+            accessStatus.setStatus(AccessStatus.Status.UNKNOWN.name());
+            accessStatus.setMessage("No credentials supplied, unknown user.");
+        } else {
+            final String identity = currentUser.getIdentity();
+            accessStatus.setIdentity(identity);
+            accessStatus.setStatus(AccessStatus.Status.ACTIVE.name());
+            accessStatus.setMessage("You are logged in.");
         }
 
         return generateOkResponse(accessStatus).build();

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
----------------------------------------------------------------------
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 58ee742..1e6602e 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
@@ -56,9 +56,9 @@ public class AuthorizableApplicationResource extends ApplicationResource {
         authorizeBucketAccess(actionType, bucketItem.getBucketIdentifier());
     }
 
-    protected Set<String> getAuthorizedBucketIds() {
+    protected Set<String> getAuthorizedBucketIds(RequestAction actionType) {
         return authorizationService
-                .getAuthorizedResources(RequestAction.READ, ResourceType.Bucket)
+                .getAuthorizedResources(actionType, ResourceType.Bucket)
                 .stream()
                 .map(AuthorizableApplicationResource::extractBucketIdFromResource)
                 .filter(Objects::nonNull)

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
index 5835ea0..19ae7f2 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
@@ -22,19 +22,19 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.field.Fields;
+import org.apache.nifi.registry.params.SortParameter;
 import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.bucket.Bucket;
-import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
 import org.apache.nifi.registry.service.QueryParameters;
-import org.apache.nifi.registry.params.SortParameter;
+import org.apache.nifi.registry.service.RegistryService;
 import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.bucket.Bucket;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -55,6 +55,9 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
 
@@ -101,6 +104,7 @@ public class BucketResource extends AuthorizableApplicationResource {
     public Response createBucket(final Bucket bucket) {
         authorizeAccess(RequestAction.WRITE);
         final Bucket createdBucket = registryService.createBucket(bucket);
+        populateBucketAuthorizedActions(createdBucket, RequestAction.WRITE);
         linkService.populateBucketLinks(createdBucket);
         return Response.status(Response.Status.OK).entity(createdBucket).build();
     }
@@ -122,9 +126,7 @@ public class BucketResource extends AuthorizableApplicationResource {
             @ApiParam(value = SortParameter.API_PARAM_DESCRIPTION, format = "field:order", allowMultiple = true, example = "name:ASC")
             @QueryParam("sort")
             final List<String> sortParameters) {
-        authorizeAccess(RequestAction.READ);
-
-        final Set<String> authorizedBucketIds = getAuthorizedBucketIds();
+        final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
 
         if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
             // not authorized for any bucket, return empty list of items
@@ -137,6 +139,7 @@ public class BucketResource extends AuthorizableApplicationResource {
         }
 
         final List<Bucket> buckets = registryService.getBuckets(paramsBuilder.build(), authorizedBucketIds);
+        populateBucketAuthorizedActions(buckets, RequestAction.READ);
         linkService.populateBucketLinks(buckets);
 
         return Response.status(Response.Status.OK).entity(buckets).build();
@@ -161,6 +164,7 @@ public class BucketResource extends AuthorizableApplicationResource {
 
         authorizeBucketAccess(RequestAction.READ, bucketId);
         final Bucket bucket = registryService.getBucket(bucketId);
+        populateBucketAuthorizedActions(bucket, RequestAction.READ);
         linkService.populateBucketLinks(bucket);
 
         if (bucket.getVersionedFlows() != null) {
@@ -206,6 +210,7 @@ public class BucketResource extends AuthorizableApplicationResource {
         authorizeBucketAccess(RequestAction.WRITE, bucketId);
 
         final Bucket updatedBucket = registryService.updateBucket(bucket);
+        populateBucketAuthorizedActions(updatedBucket, RequestAction.WRITE);
         linkService.populateBucketLinks(updatedBucket);
         return Response.status(Response.Status.OK).entity(updatedBucket).build();
     }
@@ -250,11 +255,57 @@ public class BucketResource extends AuthorizableApplicationResource {
         return Response.status(Response.Status.OK).entity(fields).build();
     }
 
-    private void authorizeAccess(RequestAction actionType) {
+    private void authorizeAccess(RequestAction actionType) throws AccessDeniedException {
         authorizationService.authorizeAccess(lookup -> {
             final Authorizable bucketsAuthorizable = lookup.getBucketsAuthorizable();
             bucketsAuthorizable.authorize(authorizer, actionType, NiFiUserUtils.getNiFiUser());
         });
     }
 
+    private void populateBucketAuthorizedActions(Collection<Bucket> buckets, RequestAction... knownAuthorizedActions) {
+
+        EnumSet<RequestAction> knownActions = EnumSet.noneOf(RequestAction.class);
+        knownActions.addAll(Arrays.asList(knownAuthorizedActions));
+
+        EnumSet<RequestAction> unknownActions = EnumSet.allOf(RequestAction.class);
+        unknownActions.removeAll(knownActions);
+
+        // if the user has an authorized action on the top-level /buckets resource, that applies to all buckets
+        for (RequestAction action : unknownActions) {
+            try {
+                authorizeAccess(action);
+                knownActions.add(action);
+            } catch (AccessDeniedException e) {
+                // not authorized, do nothing
+            }
+        }
+
+        // now for each bucket, add the known actions and check if the user has additional authorized actions for that bucket
+        for (Bucket bucket : buckets) {
+            populateBucketAuthorizedActions(bucket, knownAuthorizedActions);
+        }
+    }
+
+    private void populateBucketAuthorizedActions(Bucket bucket, RequestAction... knownAuthorizedActions) {
+
+        EnumSet<RequestAction> knownActions = EnumSet.noneOf(RequestAction.class);
+        knownActions.addAll(Arrays.asList(knownAuthorizedActions));
+
+        EnumSet<RequestAction> unknownActions = EnumSet.allOf(RequestAction.class);
+        unknownActions.removeAll(knownActions);
+
+        for (RequestAction action : knownAuthorizedActions) {
+            bucket.addAuthorizedAction(action.toString());
+        }
+
+        for (RequestAction action : unknownActions) {
+            try {
+                authorizeBucketAccess(action, bucket.getIdentifier());
+                bucket.addAuthorizedAction(action.toString());
+            } catch (AccessDeniedException e) {
+                // not authorized, do nothing
+            }
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
----------------------------------------------------------------------
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 8b8a3ed..5702540 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
@@ -19,15 +19,15 @@ package org.apache.nifi.registry.web.api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.field.Fields;
+import org.apache.nifi.registry.params.SortParameter;
 import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
 import org.apache.nifi.registry.service.QueryParameters;
-import org.apache.nifi.registry.params.SortParameter;
+import org.apache.nifi.registry.service.RegistryService;
 import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.field.Fields;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -89,7 +89,7 @@ public class ItemResource extends AuthorizableApplicationResource {
             @QueryParam("sort")
             final List<String> sortParameters) {
 
-        Set<String> authorizedBucketIds = getAuthorizedBucketIds();
+        Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
 
         if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
             // not authorized for any bucket, return empty list of items

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java
index d0cb879..119b71f 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java
@@ -20,12 +20,12 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import org.apache.nifi.registry.model.authorization.Resource;
 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.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.model.authorization.Resource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -57,13 +57,6 @@ public class ResourceResource extends AuthorizableApplicationResource {
         super(authorizer, authorizationService);
     }
 
-    private void authorizeResource() {
-        authorizationService.authorizeAccess(lookup -> {
-            final Authorizable resource = lookup.getResourcesAuthorizable();
-            resource.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
-        });
-    }
-
     /**
      * Gets the available resources that support access/authorization policies.
      *
@@ -88,4 +81,10 @@ public class ResourceResource extends AuthorizableApplicationResource {
         return generateOkResponse(resources).build();
     }
 
+    private void authorizeResource() {
+        authorizationService.authorizeAccess(lookup -> {
+            final Authorizable resource = lookup.getResourcesAuthorizable();
+            resource.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
+        });
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
new file mode 100644
index 0000000..3d2e4a1
--- /dev/null
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.web.mapper;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+@Component
+@Provider
+@Produces(MediaType.APPLICATION_JSON)
+public class NiFiRegistryJsonProvider extends JacksonJaxbJsonProvider {
+
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    static {
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        mapper.setPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));
+        mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory()));
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+    public NiFiRegistryJsonProvider() {
+        super();
+        setMapper(mapper);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ObjectMapperContextResolver.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ObjectMapperContextResolver.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ObjectMapperContextResolver.java
deleted file mode 100644
index f5f3ff8..0000000
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ObjectMapperContextResolver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.registry.web.mapper;
-
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonInclude.Value;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
-import org.springframework.stereotype.Component;
-
-import javax.ws.rs.ext.ContextResolver;
-import javax.ws.rs.ext.Provider;
-
-@Component
-@Provider
-public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
-
-    private final ObjectMapper mapper;
-
-    public ObjectMapperContextResolver() throws Exception {
-        mapper = new ObjectMapper();
-        mapper.setPropertyInclusion(Value.construct(Include.NON_NULL, Include.NON_NULL));
-        mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory()));
-    }
-
-    @Override
-    public ObjectMapper getContext(Class<?> objectType) {
-        return mapper;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
index e0eddba..ce81ee2 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
@@ -50,8 +50,8 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
 
     @Autowired private NiFiRegistryProperties properties;
 
-    @Autowired X509CertificateExtractor certificateExtractor;
-    @Autowired X509PrincipalExtractor principalExtractor;
+    @Autowired private X509CertificateExtractor certificateExtractor;
+    @Autowired private X509PrincipalExtractor principalExtractor;
     @Autowired private X509AuthenticationProvider x509AuthenticationProvider;
     private X509AuthenticationFilter x509AuthenticationFilter;
 
@@ -74,7 +74,7 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
         // the /access/download-token endpoints)
         webSecurity
                 .ignoring()
-                .antMatchers("/access", "/access/config", "/access/token");
+                .antMatchers( "/access/token");
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
index 15477f9..40ca558 100644
--- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
+++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
@@ -59,16 +59,19 @@ public class BucketsIT extends UnsecuredITBase {
                 "\"name\":\"Bucket 1\"," +
                 "\"createdTimestamp\":1505091060000," +
                 "\"description\":\"This is test bucket 1\"," +
+                "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," +
                 "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/1\"}}," +
                 "{\"identifier\":\"2\"," +
                 "\"name\":\"Bucket 2\"," +
                 "\"createdTimestamp\":1505091120000," +
                 "\"description\":\"This is test bucket 2\"," +
+                "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," +
                 "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/2\"}}," +
                 "{\"identifier\":\"3\"," +
                 "\"name\":\"Bucket 3\"," +
                 "\"createdTimestamp\":1505091180000," +
                 "\"description\":\"This is test bucket 3\"," +
+                "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," +
                 "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/3\"}}" +
                 "]";
 
@@ -82,6 +85,7 @@ public class BucketsIT extends UnsecuredITBase {
         // Then: the pre-populated list of buckets is returned
 
         JSONAssert.assertEquals(expected, bucketsJson, false);
+        assertTrue(!bucketsJson.contains("null")); // JSON serialization from the server should not include null fields, such as "versionedFlows": null
     }
 
     @Test
@@ -202,7 +206,8 @@ public class BucketsIT extends UnsecuredITBase {
         // Then: the body of the server response matches the bucket that was deleted
         //  and: the bucket is no longer accessible (resource not found)
 
-        createdBucket.setLink(null); // self URI will not be present in deletedBucket
+        createdBucket.setAuthorizedActions(null); // authorizedActions will not be present in deletedBucket
+        createdBucket.setLink(null); // links will not be present in deletedBucket
         assertBucketsEqual(createdBucket, deletedBucket, true);
 
         final Response response = client
@@ -239,6 +244,7 @@ public class BucketsIT extends UnsecuredITBase {
         if (checkServerSetFields) {
             assertEquals(expected.getIdentifier(), actual.getIdentifier());
             assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());
+            assertEquals(expected.getAuthorizedActions(), actual.getAuthorizedActions());
             assertEquals(expected.getLink(), actual.getLink());
         }
     }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
index 5b12074..e1d396e 100644
--- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
+++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/IntegrationTestBase.java
@@ -16,8 +16,14 @@
  */
 package org.apache.nifi.registry.web.api;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
 import org.apache.nifi.registry.client.NiFiRegistryClientConfig;
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.glassfish.jersey.client.ClientConfig;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.embedded.LocalServerPort;
@@ -151,13 +157,17 @@ public abstract class IntegrationTestBase {
 
     private static Client createClientFromConfig(NiFiRegistryClientConfig registryClientConfig) {
 
-        final SSLContext sslContext = registryClientConfig.getSslContext();
-        final HostnameVerifier hostnameVerifier = registryClientConfig.getHostnameVerifier();
+        final ClientConfig clientConfig = new ClientConfig();
+        clientConfig.register(jacksonJaxbJsonProvider());
+
+        final ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(clientConfig);
 
-        final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
+        final SSLContext sslContext = registryClientConfig.getSslContext();
         if (sslContext != null) {
             clientBuilder.sslContext(sslContext);
         }
+
+        final HostnameVerifier hostnameVerifier = registryClientConfig.getHostnameVerifier();
         if (hostnameVerifier != null) {
             clientBuilder.hostnameVerifier(hostnameVerifier);
         }
@@ -165,4 +175,17 @@ public abstract class IntegrationTestBase {
         return clientBuilder.build();
     }
 
+    private static JacksonJaxbJsonProvider jacksonJaxbJsonProvider() {
+        JacksonJaxbJsonProvider jacksonJaxbJsonProvider = new JacksonJaxbJsonProvider();
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));
+        mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory()));
+        // Ignore unknown properties so that deployed client remain compatible with future versions of NiFi Registry that add new fields
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+        jacksonJaxbJsonProvider.setMapper(mapper);
+        return jacksonJaxbJsonProvider;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/9b41af80/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
index 5f5af59..d6b94c2 100644
--- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
+++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
@@ -18,7 +18,9 @@ package org.apache.nifi.registry.web.api;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.SecureLdapTestApiApplication;
+import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.extension.ExtensionManager;
+import org.apache.nifi.registry.model.authorization.AccessPolicy;
 import org.apache.nifi.registry.model.authorization.Tenant;
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
 import org.apache.nifi.registry.security.authorization.Authorizer;
@@ -43,8 +45,13 @@ import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -82,7 +89,7 @@ public class SecureLdapIT extends IntegrationTestBase {
 
     }
 
-    private String authToken;
+    private String adminAuthToken;
 
     @Before
     public void generateAuthToken() {
@@ -93,11 +100,11 @@ public class SecureLdapIT extends IntegrationTestBase {
                 .target(createURL("access/token"))
                 .request()
                 .post(Entity.form(form), String.class);
-        authToken = token;
+        adminAuthToken = token;
     }
 
     @Test
-    public void testTokenGeneration() throws Exception {
+    public void testTokenGenerationAndAccessStatus() throws Exception {
 
         // Note: this test intentionally does not use the token generated
         // for nifiadmin by the @Before method
@@ -170,7 +177,7 @@ public class SecureLdapIT extends IntegrationTestBase {
         final String usersJson = client
                 .target(createURL("tenants/users"))
                 .request()
-                .header("Authorization", "Bearer " + authToken)
+                .header("Authorization", "Bearer " + adminAuthToken)
                 .get(String.class);
 
         // Then: the server returns a list of all users (see test-ldap-data.ldif)
@@ -191,14 +198,14 @@ public class SecureLdapIT extends IntegrationTestBase {
         final String groupsJson = client
                 .target(createURL("tenants/user-groups"))
                 .request()
-                .header("Authorization", "Bearer " + authToken)
+                .header("Authorization", "Bearer " + adminAuthToken)
                 .get(String.class);
 
         // Then: the server returns a list of all users (see test-ldap-data.ldif)
         JSONAssert.assertEquals(expectedJson, groupsJson, false);
     }
 
-
+    @Test
     public void testCreateTenantFails() throws Exception {
 
         // Given: the server has been configured with the LdapUserGroupProvider, which is non-configurable,
@@ -210,21 +217,169 @@ public class SecureLdapIT extends IntegrationTestBase {
         final Response createUserResponse = client
                 .target(createURL("tenants/users"))
                 .request()
-                .header("Authorization", "Bearer " + authToken)
+                .header("Authorization", "Bearer " + adminAuthToken)
                 .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class);
 
         // Then: an error is returned
-        assertEquals(405, createUserResponse.getStatus());
+        assertEquals(409, createUserResponse.getStatus());
 
         // When: the POST /tenants/users endpoint is accessed
         final Response createUserGroupResponse = client
                 .target(createURL("tenants/user-groups"))
                 .request()
-                .header("Authorization", "Bearer " + authToken)
+                .header("Authorization", "Bearer " + adminAuthToken)
                 .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class);
 
         // Then: an error is returned because the UserGroupProvider is non-configurable
-        assertEquals(405, createUserGroupResponse.getStatus());
+        assertEquals(409, createUserGroupResponse.getStatus());
+    }
+
+    @Test
+    public void testAccessPolicyCreation() throws Exception {
+
+        // Given: the server has been configured with an initial admin "nifiadmin" and a user with no accessPolicies "nobel"
+        String nobelId = getTenantIdentifierByIdentity("nobel");
+        String chemistsId = getTenantIdentifierByIdentity("chemists"); // a group containing user "nobel"
+        final Form form = new Form()
+                .param("username", "nobel")
+                .param("password", "password");
+        final String nobelAuthToken = client
+                .target(createURL("access/token"))
+                .request()
+                .post(Entity.form(form), String.class);
+
+
+        // When: nifiadmin creates a bucket
+        final Bucket bucket = new Bucket();
+        bucket.setName("Integration Test Bucket");
+        bucket.setDescription("A bucket created by an integration test.");
+        Response adminCreatesBucketResponse = client
+                .target(createURL("buckets"))
+                .request()
+                .header("Authorization", "Bearer " + adminAuthToken)
+                .post(Entity.entity(bucket, MediaType.APPLICATION_JSON), Response.class);
+
+        // Then: the server returns a 200 OK
+        assertEquals(200, adminCreatesBucketResponse.getStatus());
+        Bucket createdBucket = adminCreatesBucketResponse.readEntity(Bucket.class);
+
+
+        // When: user nobel initial queries /buckets
+        final Bucket[] buckets1 = client
+                .target(createURL("buckets"))
+                .request()
+                .header("Authorization", "Bearer " + nobelAuthToken)
+                .get(Bucket[].class);
+
+        // Then: an empty list is returned (nobel has no read access yet)
+        assertNotNull(buckets1);
+        assertEquals(0, buckets1.length);
+
+
+        // When: nifiadmin grants read access on createdBucket to 'chemists' a group containing nobel
+        Set<String> policiesToCleanup = new HashSet<>();
+        AccessPolicy readPolicy = new AccessPolicy();
+        readPolicy.setResource("/buckets/" + createdBucket.getIdentifier());
+        readPolicy.setAction("read");
+        readPolicy.addUserGroups(Arrays.asList(new Tenant(chemistsId, "chemists")));
+        Response adminGrantsReadAccessResponse = client
+                .target(createURL("policies"))
+                .request()
+                .header("Authorization", "Bearer " + adminAuthToken)
+                .post(Entity.entity(readPolicy, MediaType.APPLICATION_JSON), Response.class);
+
+        // Then: the server returns a 201 Created
+        assertEquals(201, adminGrantsReadAccessResponse.getStatus());
+        policiesToCleanup.add(adminGrantsReadAccessResponse.readEntity(AccessPolicy.class).getIdentifier());
+
+        try {
+
+            // When: user nobel re-queries /buckets
+            final Bucket[] buckets2 = client
+                    .target(createURL("buckets"))
+                    .request()
+                    .header("Authorization", "Bearer " + nobelAuthToken)
+                    .get(Bucket[].class);
+
+            // Then: the created bucket is now present
+            assertNotNull(buckets2);
+            assertEquals(1, buckets2.length);
+            assertEquals(createdBucket.getIdentifier(), buckets2[0].getIdentifier());
+            assertEquals(1, buckets2[0].getAuthorizedActions().size());
+            assertTrue(buckets2[0].getAuthorizedActions().contains("read"));
+
+
+            // When: nifiadmin grants write access on createdBucket to user 'nobel'
+            AccessPolicy writePolicy = new AccessPolicy();
+            writePolicy.setResource("/buckets/" + createdBucket.getIdentifier());
+            writePolicy.setAction("write");
+            writePolicy.addUsers(Arrays.asList(new Tenant(nobelId, "nobel")));
+            Response adminGrantsWriteAccessResponse = client
+                    .target(createURL("policies"))
+                    .request()
+                    .header("Authorization", "Bearer " + adminAuthToken)
+                    .post(Entity.entity(writePolicy, MediaType.APPLICATION_JSON), Response.class);
+
+            // Then: the server returns a 201 Created
+            assertEquals(201, adminGrantsWriteAccessResponse.getStatus());
+            policiesToCleanup.add(adminGrantsWriteAccessResponse.readEntity(AccessPolicy.class).getIdentifier());
+
+
+            // When: user nobel re-queries /buckets
+            final Bucket[] buckets3 = client
+                    .target(createURL("buckets"))
+                    .request()
+                    .header("Authorization", "Bearer " + nobelAuthToken)
+                    .get(Bucket[].class);
+
+            // Then: the authorizedActions are updated
+            assertNotNull(buckets3);
+            assertEquals(1, buckets3.length);
+            assertEquals(createdBucket.getIdentifier(), buckets3[0].getIdentifier());
+            assertEquals(2, buckets3[0].getAuthorizedActions().size());
+            assertTrue(buckets3[0].getAuthorizedActions().contains("read"));
+            assertTrue(buckets3[0].getAuthorizedActions().contains("write"));
+
+        } finally {
+            // Teardown: delete the policy we made in case other tests assume they are starting with no policies
+            for (String policyId : policiesToCleanup) {
+                Response adminDeletesPolicyResponse = client
+                        .target(createURL("policies/" + policyId))
+                        .request()
+                        .header("Authorization", "Bearer " + adminAuthToken)
+                        .delete(Response.class);
+                assertEquals(200, adminDeletesPolicyResponse.getStatus());
+            }
+        }
+
+    }
+
+    /** A helper method to lookup identifiers for tenant identities
+     *
+     * @param tenantIdentity - the identity to lookup
+     * @return A string containing the identifier of the tenant, or null if the tenant identity is not found.
+     */
+    private String getTenantIdentifierByIdentity(String tenantIdentity) {
+
+        final Tenant[] users = client
+                .target(createURL("tenants/users"))
+                .request()
+                .header("Authorization", "Bearer " + adminAuthToken)
+                .get(Tenant[].class);
+
+        final Tenant[] groups = client
+                .target(createURL("tenants/user-groups"))
+                .request()
+                .header("Authorization", "Bearer " + adminAuthToken)
+                .get(Tenant[].class);
+
+
+        final Tenant matchedTenant = Stream.concat(Arrays.stream(users), Arrays.stream(groups))
+                .filter(tenant -> tenant.getIdentity().equalsIgnoreCase(tenantIdentity))
+                .findFirst()
+                .orElse(null);
+
+        return matchedTenant != null ? matchedTenant.getIdentifier() : null;
     }
 
 }