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