NIFIREG-58 Add current user permissions For authorizable resources, such as tenants and buckets and bucket items, add a permissions fields that indicates actions available to the current user such as read, write, delete.
Adds permission details to the response of the GET /access endpoint of the Web API that shows the available action to top level resources, such as /buckets, /tenants, and /policies. Add a configurable flag to tenants indicating if they are configurable or read-only based on the Authorizer & UserGroupProvider that is managing them. This closes #43. Signed-off-by: Bryan Bende <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/04cf2322 Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/04cf2322 Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/04cf2322 Branch: refs/heads/master Commit: 04cf232206f5e67d37dbd263e6995d66be0dd7a0 Parents: a931858 Author: Kevin Doran <[email protected]> Authored: Wed Nov 29 15:47:49 2017 -0500 Committer: Bryan Bende <[email protected]> Committed: Mon Dec 11 10:12:16 2017 -0500 ---------------------------------------------------------------------- .../apache/nifi/registry/client/UserClient.java | 10 +- .../registry/client/impl/JerseyUserClient.java | 6 +- .../org/apache/nifi/registry/bucket/Bucket.java | 24 +- .../apache/nifi/registry/bucket/BucketItem.java | 11 + .../authorization/AccessPolicySummary.java | 2 +- .../model/authorization/AccessStatus.java | 69 ---- .../model/authorization/CurrentUser.java | 96 ++++++ .../model/authorization/Permissions.java | 121 +++++++ .../registry/model/authorization/Tenant.java | 16 +- .../nifi/registry/model/authorization/User.java | 2 +- .../registry/model/authorization/UserGroup.java | 2 +- .../StandardAuthorizableLookup.java | 5 +- .../authorization/resource/ResourceFactory.java | 3 +- .../ldap/tenants/LdapUserGroupProvider.java | 2 +- .../registry/service/AuthorizationService.java | 163 +++++---- .../service/AuthorizationServiceSpec.groovy | 36 -- .../registry/web/api/AccessPolicyResource.java | 2 +- .../nifi/registry/web/api/AccessResource.java | 38 +-- .../registry/web/api/BucketFlowResource.java | 20 +- .../nifi/registry/web/api/BucketResource.java | 62 +--- .../nifi/registry/web/api/ItemResource.java | 6 + .../security/NiFiRegistrySecurityConfig.java | 35 +- .../web/security/PermissionsService.java | 92 +++++ .../apache/nifi/registry/web/api/BucketsIT.java | 21 +- .../apache/nifi/registry/web/api/FlowsIT.java | 76 +---- .../registry/web/api/IntegrationTestUtils.java | 120 +++++++ .../nifi/registry/web/api/SecureFileIT.java | 9 +- .../nifi/registry/web/api/SecureKerberosIT.java | 44 ++- .../nifi/registry/web/api/SecureLdapIT.java | 338 ++++++++++++++----- .../web/api/SecureNiFiRegistryClientIT.java | 10 +- .../web/api/UnsecuredNiFiRegistryClientIT.java | 12 +- 31 files changed, 989 insertions(+), 464 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/UserClient.java ---------------------------------------------------------------------- diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/UserClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/UserClient.java index 5529f60..99ce08d 100644 --- a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/UserClient.java +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/UserClient.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.registry.client; -import org.apache.nifi.registry.model.authorization.AccessStatus; +import org.apache.nifi.registry.model.authorization.CurrentUser; import java.io.IOException; @@ -31,12 +31,12 @@ public interface UserClient { * If the UserClient was obtained without proxied entities, then it would represent the identity of the certificate * in the keystore used by the client. * - * If the registry is not in secure mode, or if the user is unauthorized for any reason, an exception - * will be thrown. + * If the registry is not in secure mode, the anonymous identity is expected to be returned along with a flag indicating + * the user is anonymous. * * @return the access status of the current user - * @throws NiFiRegistryException if the user is unauthorized, or the proxying user is not a valid proxy, or nifi-registry is not secured + * @throws NiFiRegistryException if the proxying user is not a valid proxy or identity claim is otherwise invalid */ - AccessStatus getAccessStatus() throws NiFiRegistryException, IOException; + CurrentUser getAccessStatus() throws NiFiRegistryException, IOException; } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyUserClient.java ---------------------------------------------------------------------- diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyUserClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyUserClient.java index b5c9770..1167266 100644 --- a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyUserClient.java +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyUserClient.java @@ -18,7 +18,7 @@ package org.apache.nifi.registry.client.impl; import org.apache.nifi.registry.client.NiFiRegistryException; import org.apache.nifi.registry.client.UserClient; -import org.apache.nifi.registry.model.authorization.AccessStatus; +import org.apache.nifi.registry.model.authorization.CurrentUser; import javax.ws.rs.client.WebTarget; import java.io.IOException; @@ -39,9 +39,9 @@ public class JerseyUserClient extends AbstractJerseyClient implements UserClient } @Override - public AccessStatus getAccessStatus() throws NiFiRegistryException, IOException { + public CurrentUser getAccessStatus() throws NiFiRegistryException, IOException { return executeAction("Error retrieving access status for the current user", () -> { - return getRequestBuilder(accessTarget).get(AccessStatus.class); + return getRequestBuilder(accessTarget).get(CurrentUser.class); }); } } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 8bf1c8b..929972e 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 @@ -19,13 +19,12 @@ 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.model.authorization.Permissions; 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; @XmlRootElement @ApiModel(value = "bucket") @@ -42,7 +41,7 @@ public class Bucket extends LinkableEntity { private String description; - private Set<String> authorizedActions; + private Permissions permissions; @ApiModelProperty("An ID to uniquely identify this object.") public String getIdentifier() { @@ -80,22 +79,13 @@ public class Bucket extends LinkableEntity { this.description = description; } - @ApiModelProperty(value = "A list of actions the client is authorized to perform for this bucket.", readOnly = true) - public Set<String> getAuthorizedActions() { - return authorizedActions; + @ApiModelProperty(value = "The access that the current user has to this bucket.", readOnly = true) + public Permissions getPermissions() { + return permissions; } - 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); - } + public void setPermissions(Permissions permissions) { + this.permissions = permissions; } @Override http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java ---------------------------------------------------------------------- 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 9a96635..9d61a62 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 @@ -19,6 +19,7 @@ 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.model.authorization.Permissions; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; @@ -51,6 +52,7 @@ public abstract class BucketItem extends LinkableEntity { @NotNull private final BucketItemType type; + private Permissions permissions; public BucketItem(final BucketItemType type) { this.type = type; @@ -124,6 +126,15 @@ public abstract class BucketItem extends LinkableEntity { return type; } + @ApiModelProperty(value = "The access that the current user has to the bucket containing this item.", readOnly = true) + public Permissions getPermissions() { + return permissions; + } + + public void setPermissions(Permissions permissions) { + this.permissions = permissions; + } + @Override public int hashCode() { return Objects.hashCode(this.getIdentifier()); http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java ---------------------------------------------------------------------- diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java index e27fbd9..662c999 100644 --- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessPolicySummary.java @@ -61,7 +61,7 @@ public class AccessPolicySummary { this.action = action; } - @ApiModelProperty("Indicates if this access policy is configurable, based on which authorizer has been configured to manage it.") + @ApiModelProperty(value = "Indicates if this access policy is configurable, based on which Authorizer has been configured to manage it.", readOnly = true) public Boolean getConfigurable() { return configurable; } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java ---------------------------------------------------------------------- diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java deleted file mode 100644 index 2c66387..0000000 --- a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/AccessStatus.java +++ /dev/null @@ -1,69 +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.model.authorization; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; - -@ApiModel("accessStatus") -public class AccessStatus { - - public static enum Status { - UNKNOWN, - ACTIVE - } - - private String identity; - private Status status; - private String message; - - @ApiModelProperty( - value = "The user identity.", - readOnly = true - ) - public String getIdentity() { - return identity; - } - - public void setIdentity(String identity) { - this.identity = identity; - } - - @ApiModelProperty( - value = "The user access status.", - readOnly = true - ) - public String getStatus() { - return status.toString(); - } - - public void setStatus(String status) { - this.status = Status.valueOf(status); - } - - @ApiModelProperty( - value = "Additional details about the user access status.", - readOnly = true - ) - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/CurrentUser.java ---------------------------------------------------------------------- diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/CurrentUser.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/CurrentUser.java new file mode 100644 index 0000000..7dbf932 --- /dev/null +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/CurrentUser.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.model.authorization; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel("currentUser") +public class CurrentUser { + + private String identity; + private boolean anonymous; + + private Permissions administrationPermissions; + private Permissions bucketsPermissions; + private Permissions tenantsPermissions; + private Permissions policiesPermissions; + private Permissions resourcesPermissions; + + @ApiModelProperty("The identity of the current user") + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + @ApiModelProperty("Indicates if the current user is anonymous") + public boolean isAnonymous() { + return anonymous; + } + + public void setAnonymous(boolean anonymous) { + this.anonymous = anonymous; + } + + @ApiModelProperty("The access that the current user has to the administration section of the NiFi Regsitry UI") + public Permissions getAdministrationPermissions() { + return administrationPermissions; + } + + public void setAdministrationPermissions(Permissions administrationPermissions) { + this.administrationPermissions = administrationPermissions; + } + + @ApiModelProperty("The access that the current user has to the top level /buckets resource of this NiFi Registry") + public Permissions getBucketsPermissions() { + return bucketsPermissions; + } + + public void setBucketsPermissions(Permissions bucketsPermissions) { + this.bucketsPermissions = bucketsPermissions; + } + + @ApiModelProperty("The access that the current user has to the top level /tenants resource of this NiFi Registry") + public Permissions getTenantsPermissions() { + return tenantsPermissions; + } + + public void setTenantsPermissions(Permissions tenantsPermissions) { + this.tenantsPermissions = tenantsPermissions; + } + + @ApiModelProperty("The access that the current user has to the top level /policies resource of this NiFi Registry") + public Permissions getPoliciesPermissions() { + return policiesPermissions; + } + + public void setPoliciesPermissions(Permissions policiesPermissions) { + this.policiesPermissions = policiesPermissions; + } + + @ApiModelProperty("The access that the current user has to the top level /resources resource of this NiFi Registry") + public Permissions getResourcesPermissions() { + return resourcesPermissions; + } + + public void setResourcesPermissions(Permissions resourcesPermissions) { + this.resourcesPermissions = resourcesPermissions; + } +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Permissions.java ---------------------------------------------------------------------- diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Permissions.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Permissions.java new file mode 100644 index 0000000..1dabc91 --- /dev/null +++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/model/authorization/Permissions.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.model.authorization; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel("permissions") +public class Permissions { + + private boolean canRead = false; + private boolean canWrite = false; + private boolean canDelete = false; + + public Permissions() { + } + + public Permissions(Permissions permissions) { + if (permissions == null) { + throw new IllegalArgumentException("Cannot call copy constructor with null argument"); + } + + this.canRead = permissions.getCanRead(); + this.canWrite = permissions.getCanWrite(); + this.canDelete = permissions.getCanDelete(); + } + + /** + * @return Indicates whether the user can read a given resource. + */ + @ApiModelProperty( + value = "Indicates whether the user can read a given resource.", + readOnly = true + ) + public boolean getCanRead() { + return canRead; + } + + public void setCanRead(boolean canRead) { + this.canRead = canRead; + } + + public Permissions withCanRead(boolean canRead) { + setCanRead(canRead); + return this; + } + + /** + * @return Indicates whether the user can write a given resource. + */ + @ApiModelProperty( + value = "Indicates whether the user can write a given resource.", + readOnly = true + ) + public boolean getCanWrite() { + return canWrite; + } + + public void setCanWrite(boolean canWrite) { + this.canWrite = canWrite; + } + + public Permissions withCanWrite(boolean canWrite) { + setCanWrite(canWrite); + return this; + } + + /** + * @return Indicates whether the user can delete a given resource. + */ + @ApiModelProperty( + value = "Indicates whether the user can delete a given resource.", + readOnly = true + ) + public boolean getCanDelete() { + return canDelete; + } + + public void setCanDelete(boolean canDelete) { + this.canDelete = canDelete; + } + + public Permissions withCanDelete(boolean canDelete) { + setCanDelete(canDelete); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Permissions that = (Permissions) o; + + if (canRead != that.canRead) return false; + if (canWrite != that.canWrite) return false; + return canDelete == that.canDelete; + } + + @Override + public int hashCode() { + int result = (canRead ? 1 : 0); + result = 31 * result + (canWrite ? 1 : 0); + result = 31 * result + (canDelete ? 1 : 0); + return result; + } +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 87f1242..a40fc3e 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 @@ -31,6 +31,7 @@ public class Tenant { private String identifier; private String identity; + private Boolean configurable; private Set<AccessPolicySummary> accessPolicies; public Tenant() {} @@ -43,7 +44,7 @@ public class Tenant { /** * @return tenant's unique identifier */ - @ApiModelProperty(value = "The computer-generated identifier of the tenant.") + @ApiModelProperty(value = "The computer-generated identifier of the tenant.", readOnly = true) public String getIdentifier() { return identifier; } @@ -55,7 +56,7 @@ public class Tenant { /** * @return tenant's identity */ - @ApiModelProperty(value = "The identity provider's identity of the tenant.") + @ApiModelProperty(value = "The human-facing identity of the tenant. This can only be changed if the tenant is configurable.") public String getIdentity() { return identity; } @@ -64,8 +65,17 @@ public class Tenant { this.identity = identity; } + @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; + } + + public void setConfigurable(Boolean configurable) { + this.configurable = configurable; + } + @ApiModelProperty( - value = "The access policies granted to this tenant. This field is read only", + value = "The access policies granted to this tenant.", readOnly = true ) public Set<AccessPolicySummary> getAccessPolicies() { http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 449a274..a477401 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 @@ -35,7 +35,7 @@ public class User extends Tenant { } @ApiModelProperty( - value = "The groups to which the user belongs. This field is read only.", + value = "The groups to which the user belongs.", readOnly = true ) public Set<Tenant> getUserGroups() { http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 4b14a20..1d59bdd 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 @@ -41,7 +41,7 @@ public class UserGroup extends Tenant { /** * @return The users that belong to this user group. */ - @ApiModelProperty(value = "The users that belong to this user group.") + @ApiModelProperty(value = "The users that belong to this user group. This can only be changed if this group is configurable.") public Set<Tenant> getUsers() { return users; } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java ---------------------------------------------------------------------- diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java index af48908..dfd9adc 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java @@ -17,12 +17,11 @@ package org.apache.nifi.registry.security.authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.exception.ResourceNotFoundException; +import org.apache.nifi.registry.security.authorization.resource.AccessPolicyAuthorizable; import org.apache.nifi.registry.security.authorization.resource.Authorizable; 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.resource.AccessPolicyAuthorizable; -import org.apache.nifi.registry.exception.ResourceNotFoundException; import org.springframework.stereotype.Component; @Component http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java ---------------------------------------------------------------------- diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java index c12256c..52c91c5 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java @@ -82,7 +82,7 @@ public final class ResourceFactory { @Override public String getName() { - return "NiFi Resources"; + return "Resources"; } @Override @@ -126,7 +126,6 @@ public final class ResourceFactory { } }; - /** * Gets the Resource for proxying a user request. * http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java index 976d575..9c97a8e 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java @@ -369,7 +369,7 @@ public class LdapUserGroupProvider implements UserGroupProvider { } // schedule the background thread to load the users/groups - ldapSync.scheduleWithFixedDelay(() -> load(context), syncInterval, syncInterval, TimeUnit.SECONDS); + ldapSync.scheduleWithFixedDelay(() -> load(context), syncInterval, syncInterval, TimeUnit.MILLISECONDS); } catch (final AuthorizationAccessException e) { throw new SecurityProviderCreationException(e); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 79ba71c..6c380b1 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 @@ -19,6 +19,8 @@ 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.CurrentUser; +import org.apache.nifi.registry.model.authorization.Permissions; import org.apache.nifi.registry.model.authorization.Resource; import org.apache.nifi.registry.model.authorization.Tenant; import org.apache.nifi.registry.model.authorization.User; @@ -40,8 +42,10 @@ import org.apache.nifi.registry.security.authorization.UserGroupProvider; import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; 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.NiFiUser; import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.security.exception.SecurityProviderCreationException; import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException; @@ -99,24 +103,66 @@ public class AuthorizationService { authorizeAccess.authorize(authorizableLookup); } + // ---------------------- Permissions methods --------------------------------------- - // ---------------------- Tenant methods -------------------------------------------- + public CurrentUser getCurrentUser() { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final CurrentUser currentUser = new CurrentUser(); + currentUser.setIdentity(user.getIdentity()); + currentUser.setAnonymous(user.isAnonymous()); - public Tenant getTenant(String identifier) { - this.readLock.lock(); - try { - org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier); - if (user != null) { - return tenantToDTO(user); - } else { - org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier); - return tenantToDTO(group); - } - } finally { - this.readLock.unlock(); - } + final Permissions bucketsPermissions = getPermissionsForResource(authorizableLookup.getBucketsAuthorizable()); + currentUser.setBucketsPermissions(bucketsPermissions); + + final Permissions policiesPermissions = getPermissionsForResource(authorizableLookup.getPoliciesAuthorizable()); + currentUser.setPoliciesPermissions(policiesPermissions); + + final Permissions tenantsPermissions = getPermissionsForResource(authorizableLookup.getTenantsAuthorizable()); + currentUser.setTenantsPermissions(tenantsPermissions); + + final Permissions resourcesPermissions = getPermissionsForResource(authorizableLookup.getResourcesAuthorizable()); + currentUser.setResourcesPermissions(resourcesPermissions); + + final Permissions administrationPermissions = new Permissions() + .withCanRead(bucketsPermissions.getCanRead() || tenantsPermissions.getCanRead() || policiesPermissions.getCanRead()) + .withCanWrite(bucketsPermissions.getCanWrite() || tenantsPermissions.getCanWrite() || policiesPermissions.getCanWrite()) + .withCanDelete(bucketsPermissions.getCanDelete() || tenantsPermissions.getCanDelete() || policiesPermissions.getCanDelete()); + currentUser.setAdministrationPermissions(administrationPermissions); + + return currentUser; + } + + public Permissions getPermissionsForResource(Authorizable authorizableResource) { + NiFiUser user = NiFiUserUtils.getNiFiUser(); + final Permissions permissions = new Permissions(); + permissions.setCanRead(authorizableResource.isAuthorized(authorizer, RequestAction.READ, user)); + permissions.setCanWrite(authorizableResource.isAuthorized(authorizer, RequestAction.WRITE, user)); + permissions.setCanDelete(authorizableResource.isAuthorized(authorizer, RequestAction.DELETE, user)); + return permissions; } + public Permissions getPermissionsForResource(Authorizable authorizableResource, Permissions knownParentAuthorizablePermissions) { + if (knownParentAuthorizablePermissions == null) { + return getPermissionsForResource(authorizableResource); + } + + final Permissions permissions = new Permissions(knownParentAuthorizablePermissions); + NiFiUser user = NiFiUserUtils.getNiFiUser(); + + if (!permissions.getCanRead()) { + permissions.setCanRead(authorizableResource.isAuthorized(authorizer, RequestAction.READ, user)); + } + + if (!permissions.getCanWrite()) { + permissions.setCanWrite(authorizableResource.isAuthorized(authorizer, RequestAction.WRITE, user)); + } + + if (!permissions.getCanDelete()) { + permissions.setCanDelete(authorizableResource.isAuthorized(authorizer, RequestAction.DELETE, user)); + } + + return permissions; + } // ---------------------- User methods ---------------------------------------------- @@ -436,11 +482,15 @@ public class AuthorizationService { Collection<Tenant> groupsContainingUser = userGroupProvider.getGroups().stream() .filter(group -> group.getUsers().contains(userIdentifier)) - .map(AuthorizationService::tenantToDTO) + .map(this::tenantToDTO) .collect(Collectors.toList()); Collection<AccessPolicySummary> accessPolicySummaries = getAccessPolicySummariesForUser(userIdentifier); - return userToDTO(user, groupsContainingUser, accessPolicySummaries); + User userDTO = new User(user.getIdentifier(), user.getIdentity()); + userDTO.setConfigurable(AuthorizerCapabilityDetection.isUserConfigurable(authorizer, user)); + userDTO.addUserGroups(groupsContainingUser); + userDTO.addAccessPolicies(accessPolicySummaries); + return userDTO; } private org.apache.nifi.registry.model.authorization.UserGroup userGroupToDTO( @@ -448,11 +498,16 @@ public class AuthorizationService { if (userGroup == null) { return null; } + Collection<Tenant> userTenants = userGroup.getUsers() != null - ? userGroup.getUsers().stream().map(this::getTenant).collect(Collectors.toSet()) : null; + ? userGroup.getUsers().stream().map(this::tenantIdToDTO).collect(Collectors.toSet()) : null; Collection<AccessPolicySummary> accessPolicySummaries = getAccessPolicySummariesForUserGroup(userGroup.getIdentifier()); - return userGroupToDTO(userGroup, userTenants, accessPolicySummaries); + UserGroup userGroupDTO = new UserGroup(userGroup.getIdentifier(), userGroup.getName()); + userGroupDTO.setConfigurable(AuthorizerCapabilityDetection.isGroupConfigurable(authorizer, userGroup)); + userGroupDTO.addUsers(userTenants); + userGroupDTO.addAccessPolicies(accessPolicySummaries); + return userGroupDTO; } private org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyToDTO( @@ -462,15 +517,30 @@ public class AuthorizationService { } Collection<Tenant> users = accessPolicy.getUsers() != null - ? accessPolicy.getUsers().stream().map(this::getTenant).filter(Objects::nonNull).collect(Collectors.toList()) : null; + ? accessPolicy.getUsers().stream().map(this::tenantIdToDTO).filter(Objects::nonNull).collect(Collectors.toList()) : null; Collection<Tenant> userGroups = accessPolicy.getGroups() != null - ? accessPolicy.getGroups().stream().map(this::getTenant).filter(Objects::nonNull).collect(Collectors.toList()) : null; + ? accessPolicy.getGroups().stream().map(this::tenantIdToDTO).filter(Objects::nonNull).collect(Collectors.toList()) : null; Boolean isConfigurable = AuthorizerCapabilityDetection.isAccessPolicyConfigurable(authorizer, accessPolicy); return accessPolicyToDTO(accessPolicy, userGroups, users, isConfigurable); } + private Tenant tenantIdToDTO(String identifier) { + this.readLock.lock(); + try { + org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier); + if (user != null) { + return tenantToDTO(user); + } else { + org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier); + return tenantToDTO(group); + } + } finally { + this.readLock.unlock(); + } + } + private org.apache.nifi.registry.model.authorization.AccessPolicySummary accessPolicyToSummaryDTO( final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy) { if (accessPolicy == null) { @@ -487,28 +557,30 @@ public class AuthorizationService { return accessPolicySummaryDTO; } - private static Resource resourceToDTO(org.apache.nifi.registry.security.authorization.Resource resource) { - if (resource == null) { + private Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.User user) { + if (user == null) { return null; } - Resource resourceDto = new Resource(); - resourceDto.setIdentifier(resource.getIdentifier()); - resourceDto.setName(resource.getName()); - return resourceDto; + Tenant tenantDTO = new Tenant(user.getIdentifier(), user.getIdentity()); + return tenantDTO; } - private static Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.User user) { - if (user == null) { + private Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.Group group) { + if (group == null) { return null; } - return new Tenant(user.getIdentifier(), user.getIdentity()); + Tenant tenantDTO = new Tenant(group.getIdentifier(), group.getName()); + return tenantDTO; } - private static Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.Group group) { - if (group == null) { + private static Resource resourceToDTO(org.apache.nifi.registry.security.authorization.Resource resource) { + if (resource == null) { return null; } - return new Tenant(group.getIdentifier(), group.getName()); + Resource resourceDto = new Resource(); + resourceDto.setIdentifier(resource.getIdentifier()); + resourceDto.setName(resource.getName()); + return resourceDto; } private static org.apache.nifi.registry.security.authorization.User userFromDTO( @@ -522,20 +594,6 @@ public class AuthorizationService { .build(); } - 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<AccessPolicySummary> accessPolicies) { - - if (user == null) { - return null; - } - User userDTO = new User(user.getIdentifier(), user.getIdentity()); - userDTO.addUserGroups(userGroups); - userDTO.addAccessPolicies(accessPolicies); - return userDTO; - } - private static org.apache.nifi.registry.security.authorization.Group userGroupFromDTO( final org.apache.nifi.registry.model.authorization.UserGroup userGroupDTO) { if (userGroupDTO == null) { @@ -551,19 +609,6 @@ public class AuthorizationService { return groupBuilder.build(); } - 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<AccessPolicySummary> accessPolicies) { - if (userGroup == null) { - return null; - } - UserGroup userGroupDTO = new UserGroup(userGroup.getIdentifier(), userGroup.getName()); - userGroupDTO.addUsers(users); - userGroupDTO.addAccessPolicies(accessPolicies); - return userGroupDTO; - } - private static org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicyFromDTO( final org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyDTO) { org.apache.nifi.registry.security.authorization.AccessPolicy.Builder accessPolicyBuilder = http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy ---------------------------------------------------------------------- diff --git a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy index 93fd452..a7fa5eb 100644 --- a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy +++ b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy @@ -48,42 +48,6 @@ class AuthorizationServiceSpec extends Specification { authorizationService = new AuthorizationService(authorizableLookup, authorizer, registryService) } - // ----- Tenant tests ----------------------------------------------------- - - def "get tenant"() { - - when: "get tenant for existing user identifier" - userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build() - def userResult = authorizationService.getTenant("userId") - - then: "user with identifier is returned as DTO" - with(userResult) { - identifier == "userId" - identity == "username" - } - - - when: "get tenant for existing group identifier" - userGroupProvider.getGroup("groupId") >> new Group.Builder().identifier("groupId").name("groupname").build() - def groupResult = authorizationService.getTenant("groupId") - - then: "group with identifier is returned as DTO" - with(groupResult) { - identifier == "groupId" - identity == "groupname" - } - - - when: "get tenant for non-existent identifier" - userGroupProvider.getUser("id") >> null - userGroupProvider.getGroup("id") >> null - def result = authorizationService.getTenant("id") - - then: "null is returned" - result == null - - } - // ----- User tests ------------------------------------------------------- def "create user"() { http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java ---------------------------------------------------------------------- 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 b9cfbb9..84cc555 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 @@ -271,7 +271,7 @@ public class AccessPolicyResource extends AuthorizableApplicationResource { AccessPolicy createdPolicy = authorizationService.updateAccessPolicy(requestAccessPolicy); String locationUri = generateAccessPolicyUri(createdPolicy); - return generateCreatedResponse(URI.create(locationUri), createdPolicy).build(); + return generateOkResponse(createdPolicy).build(); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 02bc3db..187f976 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 @@ -23,7 +23,7 @@ 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.model.authorization.CurrentUser; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.security.authentication.AuthenticationRequest; import org.apache.nifi.registry.security.authentication.AuthenticationResponse; @@ -34,6 +34,7 @@ import org.apache.nifi.registry.security.authentication.exception.IdentityAccess import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException; import org.apache.nifi.registry.security.authorization.user.NiFiUser; import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.web.exception.UnauthorizedException; import org.apache.nifi.registry.web.security.authentication.jwt.JwtService; import org.apache.nifi.registry.web.security.authentication.kerberos.KerberosSpnegoIdentityProvider; @@ -51,6 +52,7 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -71,6 +73,7 @@ public class AccessResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(AccessResource.class); private NiFiRegistryProperties properties; + private AuthorizationService authorizationService; private JwtService jwtService; private X509IdentityProvider x509IdentityProvider; private KerberosSpnegoIdentityProvider kerberosSpnegoIdentityProvider; @@ -79,6 +82,7 @@ public class AccessResource extends ApplicationResource { @Autowired public AccessResource( NiFiRegistryProperties properties, + AuthorizationService authorizationService, JwtService jwtService, X509IdentityProvider x509IdentityProvider, @Nullable KerberosSpnegoIdentityProvider kerberosSpnegoIdentityProvider, @@ -88,43 +92,35 @@ public class AccessResource extends ApplicationResource { this.x509IdentityProvider = x509IdentityProvider; this.kerberosSpnegoIdentityProvider = kerberosSpnegoIdentityProvider; this.identityProvider = identityProvider; + this.authorizationService = authorizationService; } /** - * Gets the status the client's access. + * Gets the current client's identity and authorized permissions. * * @param httpServletRequest the servlet request - * @return A accessStatusEntity + * @return An object describing the current client identity, as determined by the server, and it's permissions. */ @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) @ApiOperation( - value = "Gets the status the client's access", - response = AccessStatus.class + value = "Returns the current client's authenticated identity and permissions to top-level resources", + response = CurrentUser.class ) @ApiResponses({ @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might be running unsecured.") }) public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) { - // only consider user specific access over https - if (!httpServletRequest.isSecure()) { - throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS."); - } - final AccessStatus accessStatus = new AccessStatus(); - - 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."); + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user == null) { + // Not expected to happen unless the nifi registry server has been seriously misconfigured. + throw new WebApplicationException(new Throwable("Unable to access details for current user.")); } - return generateOkResponse(accessStatus).build(); + final CurrentUser currentUser = authorizationService.getCurrentUser(); + + return generateOkResponse(currentUser).build(); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java ---------------------------------------------------------------------- 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 2b10919..4cd0727 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 @@ -35,6 +35,7 @@ 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.web.link.LinkService; +import org.apache.nifi.registry.web.security.PermissionsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -68,16 +69,19 @@ public class BucketFlowResource extends AuthorizableApplicationResource { private final RegistryService registryService; private final LinkService linkService; + private final PermissionsService permissionsService; @Autowired public BucketFlowResource( final RegistryService registryService, final LinkService linkService, + final PermissionsService permissionsService, final AuthorizationService authorizationService, final Authorizer authorizer) { super(authorizer, authorizationService); this.registryService = registryService; this.linkService = linkService; + this.permissionsService =permissionsService; } @POST @@ -102,6 +106,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.WRITE, bucketId); verifyPathParamsMatchBody(bucketId, flow); final VersionedFlow createdFlow = registryService.createFlow(bucketId, flow); + permissionsService.populateItemPermissions(createdFlow); linkService.populateFlowLinks(createdFlow); return Response.status(Response.Status.OK).entity(createdFlow).build(); } @@ -136,6 +141,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { } final List<VersionedFlow> flows = registryService.getFlows(bucketId); + permissionsService.populateItemPermissions(flows); linkService.populateFlowLinks(flows); return Response.status(Response.Status.OK).entity(flows).build(); @@ -166,6 +172,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.READ, bucketId); final VersionedFlow flow = registryService.getFlow(bucketId, flowId); + permissionsService.populateItemPermissions(flow); linkService.populateFlowLinks(flow); return Response.status(Response.Status.OK).entity(flow).build(); @@ -200,6 +207,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.WRITE, bucketId); final VersionedFlow updatedFlow = registryService.updateFlow(flow); + permissionsService.populateItemPermissions(updatedFlow); linkService.populateFlowLinks(updatedFlow); return Response.status(Response.Status.OK).entity(updatedFlow).build(); @@ -267,6 +275,10 @@ public class BucketFlowResource extends AuthorizableApplicationResource { if (createdSnapshot.getSnapshotMetadata() != null) { linkService.populateSnapshotLinks(createdSnapshot.getSnapshotMetadata()); } + if (createdSnapshot.getBucket() != null) { + permissionsService.populateBucketPermissions(createdSnapshot.getBucket()); + linkService.populateBucketLinks(createdSnapshot.getBucket()); + } return Response.status(Response.Status.OK).entity(createdSnapshot).build(); } @@ -331,7 +343,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { } final VersionedFlowSnapshot lastSnapshot = registryService.getFlowSnapshot(bucketId, flowId, latest.getVersion()); - populateLinks(lastSnapshot); + populateLinksAndPermissions(lastSnapshot); return Response.status(Response.Status.OK).entity(lastSnapshot).build(); } @@ -365,6 +377,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { } linkService.populateSnapshotLinks(latest); + return Response.status(Response.Status.OK).entity(latest).build(); } @@ -395,12 +408,12 @@ public class BucketFlowResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.READ, bucketId); final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketId, flowId, versionNumber); - populateLinks(snapshot); + populateLinksAndPermissions(snapshot); return Response.status(Response.Status.OK).entity(snapshot).build(); } - private void populateLinks(VersionedFlowSnapshot snapshot) { + private void populateLinksAndPermissions(VersionedFlowSnapshot snapshot) { if (snapshot.getSnapshotMetadata() != null) { linkService.populateSnapshotLinks(snapshot.getSnapshotMetadata()); } @@ -410,6 +423,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { } if (snapshot.getBucket() != null) { + permissionsService.populateBucketPermissions(snapshot.getBucket()); linkService.populateBucketLinks(snapshot.getBucket()); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 e9e4d8b..82bb172 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 @@ -35,6 +35,7 @@ import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.QueryParameters; import org.apache.nifi.registry.service.RegistryService; import org.apache.nifi.registry.web.link.LinkService; +import org.apache.nifi.registry.web.security.PermissionsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -55,9 +56,6 @@ 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; @@ -79,15 +77,19 @@ public class BucketResource extends AuthorizableApplicationResource { private final RegistryService registryService; + private final PermissionsService permissionsService; + @Autowired public BucketResource( final RegistryService registryService, final LinkService linkService, + final PermissionsService permissionsService, final AuthorizationService authorizationService, final Authorizer authorizer) { super(authorizer, authorizationService); this.registryService = registryService; this.linkService = linkService; + this.permissionsService = permissionsService; } @POST @@ -104,7 +106,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); + permissionsService.populateBucketPermissions(createdBucket); linkService.populateBucketLinks(createdBucket); return Response.status(Response.Status.OK).entity(createdBucket).build(); } @@ -139,7 +141,7 @@ public class BucketResource extends AuthorizableApplicationResource { } final List<Bucket> buckets = registryService.getBuckets(paramsBuilder.build(), authorizedBucketIds); - populateBucketAuthorizedActions(buckets, RequestAction.READ); + permissionsService.populateBucketPermissions(buckets); linkService.populateBucketLinks(buckets); return Response.status(Response.Status.OK).entity(buckets).build(); @@ -164,7 +166,7 @@ public class BucketResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.READ, bucketId); final Bucket bucket = registryService.getBucket(bucketId); - populateBucketAuthorizedActions(bucket, RequestAction.READ); + permissionsService.populateBucketPermissions(bucket); linkService.populateBucketLinks(bucket); return Response.status(Response.Status.OK).entity(bucket).build(); @@ -206,7 +208,7 @@ public class BucketResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.WRITE, bucketId); final Bucket updatedBucket = registryService.updateBucket(bucket); - populateBucketAuthorizedActions(updatedBucket, RequestAction.WRITE); + permissionsService.populateBucketPermissions(updatedBucket); linkService.populateBucketLinks(updatedBucket); return Response.status(Response.Status.OK).entity(updatedBucket).build(); } @@ -258,50 +260,4 @@ public class BucketResource extends AuthorizableApplicationResource { }); } - 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/04cf2322/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 5702540..3e839f0 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 @@ -28,6 +28,7 @@ import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.QueryParameters; import org.apache.nifi.registry.service.RegistryService; import org.apache.nifi.registry.web.link.LinkService; +import org.apache.nifi.registry.web.security.PermissionsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -61,17 +62,20 @@ public class ItemResource extends AuthorizableApplicationResource { UriInfo uriInfo; private final LinkService linkService; + private final PermissionsService permissionsService; private final RegistryService registryService; @Autowired public ItemResource( final RegistryService registryService, final LinkService linkService, + final PermissionsService permissionsService, final AuthorizationService authorizationService, final Authorizer authorizer) { super(authorizer, authorizationService); this.registryService = registryService; this.linkService = linkService; + this.permissionsService = permissionsService; } @@ -102,6 +106,7 @@ public class ItemResource extends AuthorizableApplicationResource { } final List<BucketItem> items = registryService.getBucketItems(paramsBuilder.build(), authorizedBucketIds); + permissionsService.populateItemPermissions(items); linkService.populateItemLinks(items); return Response.status(Response.Status.OK).entity(items).build(); @@ -132,6 +137,7 @@ public class ItemResource extends AuthorizableApplicationResource { } final List<BucketItem> items = registryService.getBucketItems(paramsBuilder.build(), bucketId); + permissionsService.populateItemPermissions(items); linkService.populateItemLinks(items); return Response.status(Response.Status.OK).entity(items).build(); http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 3f428ee..dc40f3b 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 @@ -35,8 +35,15 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + /** * NiFi Registry Web Api Spring security */ @@ -44,6 +51,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter { + private static final Logger logger = LoggerFactory.getLogger(NiFiRegistrySecurityConfig.class); @Autowired @@ -81,6 +89,9 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter { .authorizeRequests() .anyRequest().fullyAuthenticated() .and() + .exceptionHandling() + .authenticationEntryPoint(http401AuthenticationEntryPoint()) + .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); @@ -93,8 +104,13 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter { // otp // todo, if needed one-time password auth filter goes here - // anonymous - http.anonymous().authenticationFilter(anonymousAuthenticationFilter); + if (properties.getSslPort() == null) { + // If we are running an unsecured NiFi Registry server, add an + // anonymous authentication filter that will populate the + // authenticated, anonymous user if no other user identity + // is detected earlier in the Spring filter chain. + http.anonymous().authenticationFilter(anonymousAuthenticationFilter); + } } @Override @@ -132,4 +148,19 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter { return jwtAuthenticationProvider; } + private AuthenticationEntryPoint http401AuthenticationEntryPoint() { + // This gets used for both secured and unsecured configurations. It will be called by Spring Security if a request makes it through the filter chain without being authenticated. + // For unsecured, this should never be reached because the custom AnonymousAuthenticationFilter should always populate a fully-authenticated anonymous user + // For secured, this will cause attempt to access any API endpoint (except those explicitly ignored) without providing credentials to return a 401 Unauthorized challenge + return new AuthenticationEntryPoint() { + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException e) throws IOException, ServletException { + logger.info("AuthenticationEntryPoint invoked as no user identity credentials were found in the request."); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + }; + } + } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java ---------------------------------------------------------------------- diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java new file mode 100644 index 0000000..f3ecb11 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java @@ -0,0 +1,92 @@ +/* + * 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.security; + +import org.apache.nifi.registry.bucket.Bucket; +import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.model.authorization.Permissions; +import org.apache.nifi.registry.security.authorization.AuthorizableLookup; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.service.AuthorizationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PermissionsService { + + private AuthorizationService authorizationService; + private AuthorizableLookup authorizableLookup; + + @Autowired + public PermissionsService(AuthorizationService authorizationService, AuthorizableLookup authorizableLookup) { + this.authorizationService = authorizationService; + this.authorizableLookup = authorizableLookup; + } + + public void populateBucketPermissions(final Iterable<Bucket> buckets) { + Permissions topLevelBucketPermissions = authorizationService.getPermissionsForResource(authorizableLookup.getBucketsAuthorizable()); + buckets.forEach(b -> populateBucketPermissions(b, topLevelBucketPermissions)); + } + + public void populateBucketPermissions(final Bucket bucket) { + populateBucketPermissions(bucket, null); + } + + public void populateItemPermissions(final Iterable<? extends BucketItem> bucketItems) { + Permissions topLevelBucketPermissions = authorizationService.getPermissionsForResource(authorizableLookup.getBucketsAuthorizable()); + bucketItems.forEach(i -> populateItemPermissions(i, topLevelBucketPermissions)); + } + + public void populateItemPermissions(final BucketItem bucketItem) { + populateItemPermissions(bucketItem, null); + } + + private void populateBucketPermissions(final Bucket bucket, final Permissions knownPermissions) { + + if (bucket == null) { + return; + } + + Permissions bucketPermissions = createPermissionsForBucketId(bucket.getIdentifier(), knownPermissions); + bucket.setPermissions(bucketPermissions); + + } + + private void populateItemPermissions(final BucketItem bucketItem, final Permissions knownPermissions) { + + if (bucketItem == null) { + return; + } + + Permissions bucketItemPermissions = createPermissionsForBucketId(bucketItem.getBucketIdentifier(), knownPermissions); + bucketItem.setPermissions(bucketItemPermissions); + + } + + private Permissions createPermissionsForBucketId(String bucketId, final Permissions knownPermissions) { + + Authorizable bucketResource = authorizableLookup.getBucketAuthorizable(bucketId); + + Permissions permissions = knownPermissions == null + ? authorizationService.getPermissionsForResource(bucketResource) + : authorizationService.getPermissionsForResource(bucketResource, knownPermissions); + + return permissions; + + } + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/04cf2322/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 350c170..85b40b7 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 @@ -25,6 +25,7 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import static org.apache.nifi.registry.web.api.IntegrationTestUtils.assertBucketsEqual; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -59,19 +60,19 @@ public class BucketsIT extends UnsecuredITBase { "\"name\":\"Bucket 1\"," + "\"createdTimestamp\":1505091060000," + "\"description\":\"This is test bucket 1\"," + - "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," + + "\"permissions\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true}," + "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/1\"}}," + "{\"identifier\":\"2\"," + "\"name\":\"Bucket 2\"," + "\"createdTimestamp\":1505091120000," + "\"description\":\"This is test bucket 2\"," + - "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," + + "\"permissions\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true}," + "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/2\"}}," + "{\"identifier\":\"3\"," + "\"name\":\"Bucket 3\"," + "\"createdTimestamp\":1505091180000," + "\"description\":\"This is test bucket 3\"," + - "\"authorizedActions\":[\"read\",\"write\",\"delete\"]," + + "\"permissions\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true}," + "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/3\"}}" + "]"; @@ -203,7 +204,7 @@ 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.setAuthorizedActions(null); // authorizedActions will not be present in deletedBucket + createdBucket.setPermissions(null); // authorizedActions will not be present in deletedBucket createdBucket.setLink(null); // links will not be present in deletedBucket assertBucketsEqual(createdBucket, deletedBucket, true); @@ -234,16 +235,4 @@ public class BucketsIT extends UnsecuredITBase { } - private static void assertBucketsEqual(Bucket expected, Bucket actual, boolean checkServerSetFields) { - assertNotNull(actual); - assertEquals(expected.getName(), actual.getName()); - assertEquals(expected.getDescription(), actual.getDescription()); - 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/04cf2322/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java ---------------------------------------------------------------------- diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java index 171442a..854c9c1 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java @@ -17,7 +17,6 @@ package org.apache.nifi.registry.web.api; import org.apache.nifi.registry.bucket.BucketItemType; -import org.apache.nifi.registry.flow.VersionedComponent; import org.apache.nifi.registry.flow.VersionedFlow; import org.apache.nifi.registry.flow.VersionedFlowSnapshot; import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; @@ -30,8 +29,10 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.Set; +import static org.apache.nifi.registry.web.api.IntegrationTestUtils.assertFlowSnapshotMetadataEqual; +import static org.apache.nifi.registry.web.api.IntegrationTestUtils.assertFlowSnapshotsEqual; +import static org.apache.nifi.registry.web.api.IntegrationTestUtils.assertFlowsEqual; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -73,6 +74,7 @@ public class FlowsIT extends UnsecuredITBase { "\"createdTimestamp\":1505091360000," + "\"modifiedTimestamp\":1505091360000," + "\"type\":\"FLOW\"," + + "\"permissions\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true}," + "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/1/flows/1\"}}," + "{\"identifier\":\"2\",\"name\":\"Flow 2\"," + "\"description\":\"This is flow 2\"," + @@ -80,6 +82,7 @@ public class FlowsIT extends UnsecuredITBase { "\"createdTimestamp\":1505091360000," + "\"modifiedTimestamp\":1505091360000," + "\"type\":\"FLOW\"," + + "\"permissions\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true}," + "\"versionCount\":0," + "\"link\":{\"params\":{\"rel\":\"self\"},\"href\":\"buckets/1/flows/2\"}}" + "]"; @@ -387,73 +390,4 @@ public class FlowsIT extends UnsecuredITBase { } - private static void assertFlowsEqual(VersionedFlow expected, VersionedFlow actual, boolean checkServerSetFields) { - assertNotNull(actual); - assertEquals(expected.getName(), actual.getName()); - assertEquals(expected.getDescription(), actual.getDescription()); - assertEquals(expected.getBucketIdentifier(), actual.getBucketIdentifier()); - if (checkServerSetFields) { - assertEquals(expected.getIdentifier(), actual.getIdentifier()); - assertEquals(expected.getVersionCount(), actual.getVersionCount()); - assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp()); - assertEquals(expected.getModifiedTimestamp(), actual.getModifiedTimestamp()); - assertEquals(expected.getType(), actual.getType()); - assertEquals(expected.getLink(), actual.getLink()); - } - } - - private static void assertFlowSnapshotsEqual(VersionedFlowSnapshot expected, VersionedFlowSnapshot actual, boolean checkServerSetFields) { - - assertNotNull(actual); - - if (expected.getSnapshotMetadata() != null) { - assertFlowSnapshotMetadataEqual(expected.getSnapshotMetadata(), actual.getSnapshotMetadata(), checkServerSetFields); - } - - if (expected.getFlowContents() != null) { - assertVersionedProcessGroupsEqual(expected.getFlowContents(), actual.getFlowContents()); - } - - } - - private static void assertFlowSnapshotMetadataEqual( - VersionedFlowSnapshotMetadata expected, VersionedFlowSnapshotMetadata actual, boolean checkServerSetFields) { - - assertNotNull(actual); - assertEquals(expected.getBucketIdentifier(), actual.getBucketIdentifier()); - assertEquals(expected.getFlowIdentifier(), actual.getFlowIdentifier()); - assertEquals(expected.getVersion(), actual.getVersion()); - assertEquals(expected.getComments(), actual.getComments()); - if (checkServerSetFields) { - assertEquals(expected.getTimestamp(), actual.getTimestamp()); - } - } - - private static void assertVersionedProcessGroupsEqual(VersionedProcessGroup expected, VersionedProcessGroup actual) { - assertNotNull(actual); - - assertEquals(((VersionedComponent)expected), ((VersionedComponent)actual)); - - // Poor man's set equality assertion as we are only checking the base type and not doing a recursive check - // TODO, this would be a stronger assertion by replacing this with a true VersionedProcessGroup.equals() method that does a deep equality check - assertSetsEqual(expected.getProcessGroups(), actual.getProcessGroups()); - assertSetsEqual(expected.getRemoteProcessGroups(), actual.getRemoteProcessGroups()); - assertSetsEqual(expected.getProcessors(), actual.getProcessors()); - assertSetsEqual(expected.getInputPorts(), actual.getInputPorts()); - assertSetsEqual(expected.getOutputPorts(), actual.getOutputPorts()); - assertSetsEqual(expected.getConnections(), actual.getConnections()); - assertSetsEqual(expected.getLabels(), actual.getLabels()); - assertSetsEqual(expected.getFunnels(), actual.getFunnels()); - assertSetsEqual(expected.getControllerServices(), actual.getControllerServices()); - } - - - private static void assertSetsEqual(Set<? extends VersionedComponent> expected, Set<? extends VersionedComponent> actual) { - if (expected != null) { - assertNotNull(actual); - assertEquals(expected.size(), actual.size()); - assertTrue(actual.containsAll(expected)); - } - } - }
