blob: f5bfb01cc8e1006e044d5f1cb33f3b82948983a1 [file] [log] [blame]
/*
* 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.service;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.RegistryConfiguration;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.authorization.CurrentUser;
import org.apache.nifi.registry.authorization.Resource;
import org.apache.nifi.registry.authorization.User;
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.extension.bundle.Bundle;
import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.bundle.BundleVersion;
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
import org.apache.nifi.registry.extension.component.ExtensionMetadata;
import org.apache.nifi.registry.extension.component.TagCount;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
import org.apache.nifi.registry.extension.repo.ExtensionRepoExtensionMetadata;
import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.revision.api.InvalidRevisionException;
import org.apache.nifi.registry.revision.entity.RevisableEntity;
import org.apache.nifi.registry.revision.entity.RevisableEntityService;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
import org.apache.nifi.registry.security.authorization.RequestAction;
import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
import org.apache.nifi.registry.security.authorization.resource.Authorizable;
import org.apache.nifi.registry.security.authorization.resource.ResourceType;
import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
import org.apache.nifi.registry.service.AuthorizationService;
import org.apache.nifi.registry.service.RegistryService;
import org.apache.nifi.registry.service.extension.ExtensionService;
import org.apache.nifi.registry.web.link.LinkService;
import org.apache.nifi.registry.web.security.PermissionsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* A wrapper around the service layer that applies validation, authorization, and revision management to all services.
*
* All REST resources should access the service layer through this facade.
*/
@Service
@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Throwable.class)
public class StandardServiceFacade implements ServiceFacade {
private static final String INVALID_REVISION_MSG = "The %s you attempted to %s with id '%s' is out of date with the server. " +
"You may need to refresh your client and try again.";
public static final String USER_GROUP_ENTITY_TYPE = "User Group";
public static final String USER_ENTITY_TYPE = "User";
public static final String ACCESS_POLICY_ENTITY_TYPE = "Access Policy";
public static final String VERSIONED_FLOW_ENTITY_TYPE = "Versioned Flow";
public static final String BUCKET_ENTITY_TYPE = "Bucket";
private final RegistryService registryService;
private final ExtensionService extensionService;
private final AuthorizationService authorizationService;
private final AuthorizableLookup authorizableLookup;
private final RevisableEntityService entityService;
private final RevisionFeature revisionFeature;
private final PermissionsService permissionsService;
private final LinkService linkService;
@Autowired
public StandardServiceFacade(final RegistryService registryService,
final ExtensionService extensionService,
final AuthorizationService authorizationService,
final AuthorizableLookup authorizableLookup,
final RevisableEntityService entityService,
final RevisionFeature revisionFeature,
final PermissionsService permissionsService,
final LinkService linkService) {
this.registryService = registryService;
this.extensionService = extensionService;
this.authorizationService = authorizationService;
this.authorizableLookup = authorizableLookup;
this.entityService = entityService;
this.revisionFeature = revisionFeature;
this.permissionsService = permissionsService;
this.linkService = linkService;
}
private String currentUserIdentity() {
return NiFiUserUtils.getNiFiUserIdentity();
}
// ---------------------- Bucket methods ----------------------------------------------
@Override
public Bucket createBucket(final Bucket bucket) {
authorizeBucketsAccess(RequestAction.WRITE);
validateCreationOfRevisableEntity(bucket, BUCKET_ENTITY_TYPE);
validateIdentifierNotPresent(bucket, BUCKET_ENTITY_TYPE);
bucket.setIdentifier(UUID.randomUUID().toString());
final Bucket createdBucket = createRevisableEntity(bucket, BUCKET_ENTITY_TYPE, currentUserIdentity(),
() -> registryService.createBucket(bucket));
permissionsService.populateBucketPermissions(createdBucket);
linkService.populateLinks(createdBucket);
return createdBucket;
}
@Override
public Bucket getBucket(final String bucketIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final Bucket bucket = entityService.get(() -> registryService.getBucket(bucketIdentifier));
permissionsService.populateBucketPermissions(bucket);
linkService.populateLinks(bucket);
return bucket;
}
@Override
public List<Bucket> getBuckets() {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return Collections.emptyList();
}
final List<Bucket> buckets = entityService.getEntities(() -> registryService.getBuckets(authorizedBucketIds));
permissionsService.populateBucketPermissions(buckets);
linkService.populateLinks(buckets);
return buckets;
}
@Override
public Bucket updateBucket(final Bucket bucket) {
authorizeBucketAccess(RequestAction.WRITE, bucket.getIdentifier());
validateUpdateOfRevisableEntity(bucket, BUCKET_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
registryService.verifyBucketExists(bucket.getIdentifier());
final Bucket updatedBucket = updateRevisableEntity(bucket, BUCKET_ENTITY_TYPE, currentUserIdentity(),
() -> registryService.updateBucket(bucket));
permissionsService.populateBucketPermissions(updatedBucket);
linkService.populateLinks(updatedBucket);
return updatedBucket;
}
@Override
public Bucket deleteBucket(final String bucketIdentifier, final RevisionInfo revisionInfo) {
authorizeBucketAccess(RequestAction.DELETE, bucketIdentifier);
validateDeleteOfRevisableEntity(bucketIdentifier, revisionInfo, BUCKET_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
registryService.verifyBucketExists(bucketIdentifier);
return deleteRevisableEntity(bucketIdentifier, BUCKET_ENTITY_TYPE, revisionInfo,
() -> registryService.deleteBucket(bucketIdentifier));
}
// ---------------------- BucketItem methods ----------------------------------------------
@Override
public List<BucketItem> getBucketItems(final String bucketIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final List<BucketItem> items = registryService.getBucketItems(bucketIdentifier);
entityService.populateRevisions(items);
permissionsService.populateItemPermissions(items);
linkService.populateLinks(items);
return items;
}
@Override
public List<BucketItem> getBucketItems() {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return new ArrayList<>();
}
final List<BucketItem> items = registryService.getBucketItems(authorizedBucketIds);
entityService.populateRevisions(items);
permissionsService.populateItemPermissions(items);
linkService.populateLinks(items);
return items;
}
// ---------------------- Flow methods ----------------------------------------------
@Override
public VersionedFlow createFlow(final String bucketIdentifier, final VersionedFlow versionedFlow) {
authorizeBucketAccess(RequestAction.WRITE, bucketIdentifier);
validateCreationOfRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE);
// NOTE: Don't validate that identifier is null...
// NiFi has been sending an identifier, so we must maintain backwards compatibility
if (versionedFlow.getIdentifier() == null) {
versionedFlow.setIdentifier(UUID.randomUUID().toString());
}
final VersionedFlow createdFlow = createRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE, currentUserIdentity(),
() -> registryService.createFlow(bucketIdentifier, versionedFlow));
permissionsService.populateItemPermissions(createdFlow);
linkService.populateLinks(createdFlow);
return createdFlow;
}
@Override
public VersionedFlow getFlow(final String bucketIdentifier, final String flowIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final VersionedFlow flow = entityService.get(
() -> registryService.getFlow(bucketIdentifier, flowIdentifier));
permissionsService.populateItemPermissions(flow);
linkService.populateLinks(flow);
return flow;
}
@Override
public VersionedFlow getFlow(final String flowIdentifier) {
final VersionedFlow flow = entityService.get(() -> registryService.getFlow(flowIdentifier));
authorizeBucketAccess(RequestAction.READ, flow);
permissionsService.populateItemPermissions(flow);
linkService.populateLinks(flow);
return flow;
}
@Override
public List<VersionedFlow> getFlows(final String bucketIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final List<VersionedFlow> flows = entityService.getEntities(() -> registryService.getFlows(bucketIdentifier));
permissionsService.populateItemPermissions(flows);
linkService.populateLinks(flows);
return flows;
}
@Override
public VersionedFlow updateFlow(final VersionedFlow versionedFlow) {
authorizeBucketAccess(RequestAction.WRITE, versionedFlow);
validateUpdateOfRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
registryService.verifyFlowExists(versionedFlow.getIdentifier());
final VersionedFlow updatedFlow = updateRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE, currentUserIdentity(),
() -> registryService.updateFlow(versionedFlow));
permissionsService.populateItemPermissions(updatedFlow);
linkService.populateLinks(updatedFlow);
return updatedFlow;
}
@Override
public VersionedFlow deleteFlow(final String bucketIdentifier, final String flowIdentifier, final RevisionInfo revisionInfo) {
authorizeBucketAccess(RequestAction.DELETE, bucketIdentifier);
validateDeleteOfRevisableEntity(flowIdentifier, revisionInfo, VERSIONED_FLOW_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
registryService.verifyFlowExists(flowIdentifier);
return deleteRevisableEntity(flowIdentifier, VERSIONED_FLOW_ENTITY_TYPE, revisionInfo,
() -> registryService.deleteFlow(bucketIdentifier, flowIdentifier));
}
// ---------------------- Flow Snapshot methods ----------------------------------------------
@Override
public VersionedFlowSnapshot createFlowSnapshot(final VersionedFlowSnapshot flowSnapshot) {
authorizeBucketAccess(RequestAction.WRITE, flowSnapshot);
final VersionedFlowSnapshot createdSnapshot = registryService.createFlowSnapshot(flowSnapshot);
populateLinksAndPermissions(createdSnapshot);
return createdSnapshot;
}
@Override
public VersionedFlowSnapshot getFlowSnapshot(final String bucketIdentifier, final String flowIdentifier, final Integer version) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, version);
populateLinksAndPermissions(snapshot);
return snapshot;
}
@Override
public VersionedFlowSnapshot getFlowSnapshot(final String flowIdentifier, final Integer version) {
final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
authorizeBucketAccess(RequestAction.READ, latestMetadata);
final String bucketIdentifier = latestMetadata.getBucketIdentifier();
final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, version);
populateLinksAndPermissions(snapshot);
return snapshot;
}
@Override
public VersionedFlowSnapshot getLatestFlowSnapshot(final String bucketIdentifier, final String flowIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final VersionedFlowSnapshotMetadata latestMetadata = getLatestFlowSnapshotMetadata(bucketIdentifier, flowIdentifier);
final VersionedFlowSnapshot lastSnapshot = getFlowSnapshot(bucketIdentifier, flowIdentifier, latestMetadata.getVersion());
populateLinksAndPermissions(lastSnapshot);
return lastSnapshot;
}
@Override
public VersionedFlowSnapshot getLatestFlowSnapshot(final String flowIdentifier) {
final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
authorizeBucketAccess(RequestAction.READ, latestMetadata);
final String bucketIdentifier = latestMetadata.getBucketIdentifier();
final Integer latestVersion = latestMetadata.getVersion();
final VersionedFlowSnapshot lastSnapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, latestVersion);
populateLinksAndPermissions(lastSnapshot);
return lastSnapshot;
}
@Override
public SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(final String bucketIdentifier, final String flowIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketIdentifier, flowIdentifier);
linkService.populateLinks(snapshots);
return snapshots;
}
@Override
public SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(final String flowIdentifier) {
final VersionedFlow flow = registryService.getFlow(flowIdentifier);
authorizeBucketAccess(RequestAction.READ, flow);
final String bucketIdentifier = flow.getBucketIdentifier();
final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketIdentifier, flowIdentifier);
linkService.populateLinks(snapshots);
return snapshots;
}
@Override
public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String bucketIdentifier, final String flowIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(bucketIdentifier, flowIdentifier);
linkService.populateLinks(latest);
return latest;
}
@Override
public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String flowIdentifier) {
final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
authorizeBucketAccess(RequestAction.READ, latest);
linkService.populateLinks(latest);
return latest;
}
@Override
public VersionedFlowDifference getFlowDiff(final String bucketIdentifier, final String flowIdentifier, final Integer versionA, final Integer versionB) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
return registryService.getFlowDiff(bucketIdentifier, flowIdentifier, versionA, versionB);
}
private void populateLinksAndPermissions(final VersionedFlowSnapshot snapshot) {
if (snapshot == null) {
return;
}
if (snapshot.getSnapshotMetadata() != null) {
linkService.populateLinks(snapshot.getSnapshotMetadata());
}
if (snapshot.getFlow() != null) {
linkService.populateLinks(snapshot.getFlow());
}
if (snapshot.getBucket() != null) {
permissionsService.populateBucketPermissions(snapshot.getBucket());
linkService.populateLinks(snapshot.getBucket());
}
}
// ---------------------- Bundle methods ----------------------------------------------
@Override
public List<Bundle> getBundles(final BundleFilterParams filterParams) {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return new ArrayList<>();
}
final List<Bundle> bundles = extensionService.getBundles(authorizedBucketIds, filterParams);
permissionsService.populateItemPermissions(bundles);
linkService.populateLinks(bundles);
return bundles;
}
@Override
public List<Bundle> getBundlesByBucket(final String bucketIdentifier) {
authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
final List<Bundle> bundles = extensionService.getBundlesByBucket(bucketIdentifier);
permissionsService.populateItemPermissions(bundles);
linkService.populateLinks(bundles);
return bundles;
}
@Override
public Bundle getBundle(final String bundleIdentifier) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
permissionsService.populateItemPermissions(bundle);
linkService.populateLinks(bundle);
return bundle;
}
@Override
public Bundle deleteBundle(final String bundleIdentifier) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
authorizeBucketAccess(RequestAction.DELETE, bundle);
final Bundle deletedBundle = extensionService.deleteBundle(bundle);
permissionsService.populateItemPermissions(deletedBundle);
linkService.populateLinks(deletedBundle);
return deletedBundle;
}
// ---------------------- Bundle Version methods ----------------------------------------------
@Override
public BundleVersion createBundleVersion(final String bucketIdentifier, final BundleType bundleType,
final InputStream inputStream, final String clientSha256) throws IOException {
authorizeBucketAccess(RequestAction.WRITE, bucketIdentifier);
final BundleVersion createdBundleVersion = extensionService.createBundleVersion(
bucketIdentifier, bundleType, inputStream, clientSha256);
linkService.populateLinks(createdBundleVersion.getVersionMetadata());
linkService.populateLinks(createdBundleVersion.getBundle());
linkService.populateLinks(createdBundleVersion.getBucket());
permissionsService.populateItemPermissions(createdBundleVersion.getBundle());
return createdBundleVersion;
}
@Override
public SortedSet<BundleVersionMetadata> getBundleVersions(final BundleVersionFilterParams filterParams) {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return Collections.emptySortedSet();
}
final SortedSet<BundleVersionMetadata> bundleVersions = extensionService.getBundleVersions(authorizedBucketIds, filterParams);
linkService.populateLinks(bundleVersions);
return bundleVersions;
}
@Override
public SortedSet<BundleVersionMetadata> getBundleVersions(final String bundleIdentifier) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final SortedSet<BundleVersionMetadata> bundleVersions = extensionService.getBundleVersions(bundleIdentifier);
linkService.populateLinks(bundleVersions);
return bundleVersions;
}
@Override
public BundleVersion getBundleVersion(final String bundleIdentifier, final String version) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
linkService.populateLinks(bundleVersion);
return bundleVersion;
}
@Override
public StreamingContent getBundleVersionContent(final String bundleIdentifier, final String version) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeBundleVersionContent(bundleVersion, output);
return new StreamingContent(streamingOutput, bundleVersion.getFilename());
}
@Override
public BundleVersion deleteBundleVersion(final String bundleIdentifier, final String version) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
authorizeBucketAccess(RequestAction.DELETE, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
final BundleVersion deletedBundleVersion = extensionService.deleteBundleVersion(bundleVersion);
linkService.populateLinks(deletedBundleVersion);
return deletedBundleVersion;
}
// ---------------------- Extension methods ----------------------------------------------
@Override
public SortedSet<ExtensionMetadata> getExtensionMetadata(final ExtensionFilterParams filterParams) {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return Collections.emptySortedSet();
}
final SortedSet<ExtensionMetadata> metadata = extensionService.getExtensionMetadata(authorizedBucketIds, filterParams);
linkService.populateLinks(metadata);
return metadata;
}
@Override
public SortedSet<ExtensionMetadata> getExtensionMetadata(final ProvidedServiceAPI serviceAPI) {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return Collections.emptySortedSet();
}
final SortedSet<ExtensionMetadata> metadata = extensionService.getExtensionMetadata(authorizedBucketIds, serviceAPI);
linkService.populateLinks(metadata);
return metadata;
}
@Override
public SortedSet<ExtensionMetadata> getExtensionMetadata(final String bundleIdentifier, final String version) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
final SortedSet<ExtensionMetadata> extensions = extensionService.getExtensionMetadata(bundleVersion);
linkService.populateLinks(extensions);
return extensions;
}
@Override
public Extension getExtension(final String bundleIdentifier, final String version, final String name) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
return extensionService.getExtension(bundleVersion, name);
}
@Override
public StreamingOutput getExtensionDocs(final String bundleIdentifier, final String version, final String name) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeExtensionDocs(bundleVersion, name, output);
return streamingOutput;
}
@Override
public StreamingOutput getAdditionalDetailsDocs(final String bundleIdentifier, final String version, final String name) {
final Bundle bundle = extensionService.getBundle(bundleIdentifier);
authorizeBucketAccess(RequestAction.READ, bundle);
final String bucketIdentifier = bundle.getBucketIdentifier();
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeAdditionalDetailsDocs(bundleVersion, name, output);
return streamingOutput;
}
@Override
public SortedSet<TagCount> getExtensionTags() {
return extensionService.getExtensionTags();
}
// ---------------------- Extension Repository methods ----------------------------------------------
@Override
public SortedSet<ExtensionRepoBucket> getExtensionRepoBuckets(final URI baseUri) {
final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
// not authorized for any bucket, return empty list of items
return Collections.emptySortedSet();
}
final SortedSet<ExtensionRepoBucket> repoBuckets = extensionService.getExtensionRepoBuckets(authorizedBucketIds);
linkService.populateFullLinks(repoBuckets, baseUri);
return repoBuckets;
}
@Override
public SortedSet<ExtensionRepoGroup> getExtensionRepoGroups(final URI baseUri, final String bucketName) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final SortedSet<ExtensionRepoGroup> repoGroups = extensionService.getExtensionRepoGroups(bucket);
linkService.populateFullLinks(repoGroups, baseUri);
return extensionService.getExtensionRepoGroups(bucket);
}
@Override
public SortedSet<ExtensionRepoArtifact> getExtensionRepoArtifacts(final URI baseUri, final String bucketName, final String groupId) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final SortedSet<ExtensionRepoArtifact> repoArtifacts = extensionService.getExtensionRepoArtifacts(bucket, groupId);
linkService.populateFullLinks(repoArtifacts, baseUri);
return repoArtifacts;
}
@Override
public SortedSet<ExtensionRepoVersionSummary> getExtensionRepoVersions(final URI baseUri, final String bucketName, final String groupId,
final String artifactId) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final SortedSet<ExtensionRepoVersionSummary> repoVersions = extensionService.getExtensionRepoVersions(bucket, groupId, artifactId);
linkService.populateFullLinks(repoVersions, baseUri);
return repoVersions;
}
@Override
public ExtensionRepoVersion getExtensionRepoVersion(final URI baseUri, final String bucketName, final String groupId,
final String artifactId, final String version) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final String extensionsUri = generateResourceUri(baseUri,
"extension-repository",
bundleVersion.getBucket().getName(),
bundleVersion.getBundle().getGroupId(),
bundleVersion.getBundle().getArtifactId(),
bundleVersion.getVersionMetadata().getVersion(),
"extensions");
final String downloadUri = generateResourceUri(baseUri,
"extension-repository",
bundleVersion.getBucket().getName(),
bundleVersion.getBundle().getGroupId(),
bundleVersion.getBundle().getArtifactId(),
bundleVersion.getVersionMetadata().getVersion(),
"content");
final String sha256Uri = generateResourceUri(baseUri,
"extension-repository",
bundleVersion.getBucket().getName(),
bundleVersion.getBundle().getGroupId(),
bundleVersion.getBundle().getArtifactId(),
bundleVersion.getVersionMetadata().getVersion(),
"sha256");
final ExtensionRepoVersion repoVersion = new ExtensionRepoVersion();
repoVersion.setExtensionsLink(Link.fromUri(extensionsUri).rel("extensions").build());
repoVersion.setDownloadLink(Link.fromUri(downloadUri).rel("content").build());
repoVersion.setSha256Link(Link.fromUri(sha256Uri).rel("sha256").build());
repoVersion.setSha256Supplied(bundleVersion.getVersionMetadata().getSha256Supplied());
return repoVersion;
}
@Override
public StreamingContent getExtensionRepoVersionContent(final String bucketName, final String groupId, final String artifactId,
final String version) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeBundleVersionContent(bundleVersion, output);
return new StreamingContent(streamingOutput, bundleVersion.getFilename());
}
@Override
public String getExtensionRepoVersionSha256(final String bucketName, final String groupId, final String artifactId,
final String version) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final String sha256Hex = bundleVersion.getVersionMetadata().getSha256();
return sha256Hex;
}
@Override
public List<ExtensionRepoExtensionMetadata> getExtensionRepoExtensions(final URI baseUri, final String bucketName, final String groupId,
final String artifactId, final String version) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final SortedSet<ExtensionMetadata> extensions = extensionService.getExtensionMetadata(bundleVersion);
final List<ExtensionRepoExtensionMetadata> extensionRepoExtensions = new ArrayList<>(extensions.size());
extensions.forEach(e -> extensionRepoExtensions.add(new ExtensionRepoExtensionMetadata(e)));
linkService.populateFullLinks(extensionRepoExtensions, baseUri);
return extensionRepoExtensions;
}
@Override
public Extension getExtensionRepoExtension(final URI baseUri, final String bucketName, final String groupId,
final String artifactId, final String version, final String extensionName) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final Extension extension = extensionService.getExtension(bundleVersion, extensionName);
return extension;
}
@Override
public StreamingOutput getExtensionRepoExtensionDocs(final URI baseUri, final String bucketName, final String groupId,
final String artifactId, final String version, final String extensionName) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeExtensionDocs(bundleVersion, extensionName, output);
return streamingOutput;
}
@Override
public StreamingOutput getExtensionRepoExtensionAdditionalDocs(final URI baseUri, final String bucketName, final String groupId,
final String artifactId, final String version, final String extensionName) {
final Bucket bucket = registryService.getBucketByName(bucketName);
authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
final StreamingOutput streamingOutput = (output) -> extensionService.writeAdditionalDetailsDocs(bundleVersion, extensionName, output);
return streamingOutput;
}
// ---------------------- Field methods ----------------------------------------------
@Override
public Set<String> getBucketFields() {
return registryService.getBucketFields();
}
@Override
public Set<String> getBucketItemFields() {
return registryService.getBucketItemFields();
}
@Override
public Set<String> getFlowFields() {
return registryService.getFlowFields();
}
// ---------------------- User methods ----------------------------------------------
@Override
public User createUser(final User user) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.WRITE);
validateCreationOfRevisableEntity(user, USER_ENTITY_TYPE);
validateIdentifierNotPresent(user, USER_ENTITY_TYPE);
user.setIdentifier(UUID.randomUUID().toString());
return createRevisableEntity(user, USER_ENTITY_TYPE, currentUserIdentity(), () -> authorizationService.createUser(user));
}
@Override
public List<User> getUsers() {
verifyAuthorizerIsManaged();
authorizeTenantsAccess(RequestAction.READ);
return entityService.getEntities(() -> authorizationService.getUsers());
}
@Override
public User getUser(final String identifier) {
verifyAuthorizerIsManaged();
authorizeTenantsAccess(RequestAction.READ);
return entityService.get(() -> authorizationService.getUser(identifier));
}
@Override
public User updateUser(final User user) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.WRITE);
validateUpdateOfRevisableEntity(user, USER_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyUserExists(user.getIdentifier());
return updateRevisableEntity(user, USER_ENTITY_TYPE, currentUserIdentity(), () -> authorizationService.updateUser(user));
}
@Override
public User deleteUser(final String identifier, final RevisionInfo revisionInfo) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.DELETE);
validateDeleteOfRevisableEntity(identifier, revisionInfo, USER_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyUserExists(identifier);
return deleteRevisableEntity(identifier, USER_ENTITY_TYPE, revisionInfo, () -> authorizationService.deleteUser(identifier));
}
// ---------------------- UserGroup methods ----------------------------------------------
@Override
public UserGroup createUserGroup(final UserGroup userGroup) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.WRITE);
validateCreationOfRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE);
validateIdentifierNotPresent(userGroup, USER_GROUP_ENTITY_TYPE);
userGroup.setIdentifier(UUID.randomUUID().toString());
return createRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE, currentUserIdentity(),
() -> authorizationService.createUserGroup(userGroup));
}
@Override
public List<UserGroup> getUserGroups() {
verifyAuthorizerIsManaged();
authorizeTenantsAccess(RequestAction.READ);
return entityService.getEntities(() -> authorizationService.getUserGroups());
}
@Override
public UserGroup getUserGroup(final String identifier) {
verifyAuthorizerIsManaged();
authorizeTenantsAccess(RequestAction.READ);
return entityService.get(() -> authorizationService.getUserGroup(identifier));
}
@Override
public UserGroup updateUserGroup(final UserGroup userGroup) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.WRITE);
validateUpdateOfRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyUserGroupExists(userGroup.getIdentifier());
return updateRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE, currentUserIdentity(),
() -> authorizationService.updateUserGroup(userGroup));
}
@Override
public UserGroup deleteUserGroup(final String identifier, final RevisionInfo revisionInfo) {
verifyAuthorizerSupportsConfigurableUserGroups();
authorizeTenantsAccess(RequestAction.DELETE);
validateDeleteOfRevisableEntity(identifier, revisionInfo, USER_GROUP_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyUserGroupExists(identifier);
return deleteRevisableEntity(identifier, USER_GROUP_ENTITY_TYPE, revisionInfo,
() -> authorizationService.deleteUserGroup(identifier));
}
// ---------------------- AccessPolicy methods ----------------------------------------------
@Override
public AccessPolicy createAccessPolicy(final AccessPolicy accessPolicy) {
verifyAuthorizerSupportsConfigurablePolicies();
authorizePoliciesAccess(RequestAction.WRITE);
validateCreationOfRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE);
validateIdentifierNotPresent(accessPolicy, ACCESS_POLICY_ENTITY_TYPE);
accessPolicy.setIdentifier(UUID.randomUUID().toString());
return createRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE, currentUserIdentity(),
() -> authorizationService.createAccessPolicy(accessPolicy));
}
@Override
public AccessPolicy getAccessPolicy(final String identifier) {
verifyAuthorizerIsManaged();
authorizePoliciesAccess(RequestAction.READ);
return entityService.get(() -> authorizationService.getAccessPolicy(identifier));
}
@Override
public AccessPolicy getAccessPolicy(final String resource, final RequestAction action) {
verifyAuthorizerIsManaged();
authorizePoliciesAccess(RequestAction.READ);
return entityService.get(() -> authorizationService.getAccessPolicy(resource, action));
}
@Override
public List<AccessPolicy> getAccessPolicies() {
verifyAuthorizerIsManaged();
authorizePoliciesAccess(RequestAction.READ);
return entityService.getEntities(() -> authorizationService.getAccessPolicies());
}
@Override
public AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) {
verifyAuthorizerSupportsConfigurablePolicies();
authorizePoliciesAccess(RequestAction.WRITE);
validateUpdateOfRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyAccessPolicyExists(accessPolicy.getIdentifier());
return updateRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE, currentUserIdentity(),
() -> authorizationService.updateAccessPolicy(accessPolicy));
}
@Override
public AccessPolicy deleteAccessPolicy(final String identifier, final RevisionInfo revisionInfo) {
verifyAuthorizerSupportsConfigurablePolicies();
authorizePoliciesAccess(RequestAction.DELETE);
validateDeleteOfRevisableEntity(identifier, revisionInfo, ACCESS_POLICY_ENTITY_TYPE);
// verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
authorizationService.verifyAccessPolicyExists(identifier);
return deleteRevisableEntity(identifier, ACCESS_POLICY_ENTITY_TYPE, revisionInfo,
() -> authorizationService.deleteAccessPolicy(identifier));
}
@Override
public List<Resource> getResources() {
authorizePoliciesAccess(RequestAction.READ);
return authorizationService.getResources();
}
// ---------------------- Permission methods -----------------------------
@Override
public CurrentUser getCurrentUser() {
return authorizationService.getCurrentUser();
}
// ---------------------- Authorization methods -----------------------------
private void verifyAuthorizerIsManaged() {
authorizationService.verifyAuthorizerIsManaged();
}
private void verifyAuthorizerSupportsConfigurablePolicies() {
authorizationService.verifyAuthorizerSupportsConfigurablePolicies();
}
private void verifyAuthorizerSupportsConfigurableUserGroups() {
authorizationService.verifyAuthorizerSupportsConfigurableUserGroups();
}
// ---------------------- Configuration methods -----------------------------
@Override
public RegistryConfiguration getRegistryConfiguration() {
final RegistryConfiguration config = new RegistryConfiguration();
boolean hasAnyConfigurationAccess = false;
AccessDeniedException lastAccessDeniedException = null;
try {
final Authorizable policyAuthorizer = authorizableLookup.getPoliciesAuthorizable();
authorizationService.authorize(policyAuthorizer, RequestAction.READ);
config.setSupportsManagedAuthorizer(authorizationService.isManagedAuthorizer());
config.setSupportsConfigurableAuthorizer(authorizationService.isConfigurableAccessPolicyProvider());
hasAnyConfigurationAccess = true;
} catch (AccessDeniedException e) {
lastAccessDeniedException = e;
}
try {
authorizationService.authorize(authorizableLookup.getTenantsAuthorizable(), RequestAction.READ);
config.setSupportsConfigurableUsersAndGroups(authorizationService.isConfigurableUserGroupProvider());
hasAnyConfigurationAccess = true;
} catch (AccessDeniedException e) {
lastAccessDeniedException = e;
}
if (!hasAnyConfigurationAccess) {
// If the user doesn't have access to any configuration, then throw the exception.
// Otherwise, return what they can access.
throw lastAccessDeniedException;
}
return config;
}
// ---------------------- Helper methods -------------------------------------
private void authorizeBucketsAccess(RequestAction actionType) throws AccessDeniedException {
final Authorizable bucketsAuthorizable = authorizableLookup.getBucketsAuthorizable();
authorizationService.authorize(bucketsAuthorizable, actionType);
}
private void authorizeBucketAccess(final RequestAction actionType, final String bucketIdentifier) {
if (StringUtils.isBlank(bucketIdentifier)) {
throw new IllegalArgumentException("Unable to authorize access because bucket identifier is null or blank");
}
final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketIdentifier);
authorizationService.authorize(bucketAuthorizable, actionType);
}
private void authorizeBucketAccess(final RequestAction action, final Bundle bundle) {
// this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the bundle isn't returned
if (bundle == null) {
throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
}
authorizeBucketAccess(action, bundle.getBucketIdentifier());
}
private void authorizeBucketAccess(final RequestAction action, final VersionedFlow flow) {
// this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow isn't returned
if (flow == null) {
throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
}
authorizeBucketAccess(action, flow.getBucketIdentifier());
}
private void authorizeBucketAccess(final RequestAction action, final VersionedFlowSnapshot flowSnapshot) {
// this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow snapshot isn't returned
if (flowSnapshot == null || flowSnapshot.getSnapshotMetadata() == null) {
throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
}
authorizeBucketAccess(action, flowSnapshot.getSnapshotMetadata().getBucketIdentifier());
}
private void authorizeBucketAccess(final RequestAction action, final VersionedFlowSnapshotMetadata flowSnapshotMetadata) {
// this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow snapshot isn't returned
if (flowSnapshotMetadata == null) {
throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
}
authorizeBucketAccess(action, flowSnapshotMetadata.getBucketIdentifier());
}
private Set<String> getAuthorizedBucketIds(final RequestAction actionType) {
return authorizationService.getAuthorizedResources(actionType, ResourceType.Bucket)
.stream()
.map(StandardServiceFacade::extractBucketIdFromResource)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toSet());
}
private static String extractBucketIdFromResource(Resource resource) {
if (resource == null || resource.getIdentifier() == null || !resource.getIdentifier().startsWith("/buckets/")) {
return null;
}
final String[] pathComponents = resource.getIdentifier().split("/");
if (pathComponents.length < 3) {
return null;
}
return pathComponents[2];
}
private String generateResourceUri(final URI baseUri, final String... path) {
final URI fullUri = UriBuilder.fromUri(baseUri).segment(path).build();
return fullUri.toString();
}
private void authorizePoliciesAccess(final RequestAction actionType) {
final Authorizable policiesAuthorizable = authorizableLookup.getPoliciesAuthorizable();
authorizationService.authorize(policiesAuthorizable, actionType);
}
private void authorizeTenantsAccess(RequestAction actionType) {
final Authorizable tenantsAuthorizable = authorizableLookup.getTenantsAuthorizable();
authorizationService.authorize(tenantsAuthorizable, actionType);
}
// ---------------------- Revision Helper Methods -------------------------------------
private void validateCreationOfRevisableEntity(final RevisableEntity entity, final String entityTypeName) {
if (entity == null) {
throw new IllegalArgumentException(entityTypeName + " cannot be null");
}
// skip checking revision if feature is disabled
if (!revisionFeature.isEnabled()) {
return;
}
// NOT: restore identifier check here when we no longer needs backwards compatibility
if (entity.getRevision() == null
|| entity.getRevision().getVersion() == null
|| entity.getRevision().getVersion().longValue() != 0) {
throw new IllegalArgumentException("A revision of 0 must be specified when creating a new " + entityTypeName + ".");
}
}
/**
* NOTE: This logic should be moved back to validateCreationOfRevisableEntity once we no longer need to maintain
* backwards compatibility (i.e. on a major release like 1.0.0).
*
* Currently NiFi has been sending an identifier when creating a flow, so we need to continue to allow that.
*/
private void validateIdentifierNotPresent(final RevisableEntity entity, final String entityTypeName) {
if (entity.getIdentifier() != null) {
throw new IllegalArgumentException(entityTypeName + " identifier cannot be specified when creating a new "
+ entityTypeName.toLowerCase() + ".");
}
}
private void validateUpdateOfRevisableEntity(final RevisableEntity entity, final String entityTypeName) {
if (entity == null) {
throw new IllegalArgumentException(entityTypeName + " cannot be null");
}
// skip checking revision if feature is disabled
if (!revisionFeature.isEnabled()) {
return;
}
if (entity.getRevision() == null || entity.getRevision().getVersion() == null) {
throw new IllegalArgumentException("Revision info must be specified.");
}
}
private void validateDeleteOfRevisableEntity(final String identifier, final RevisionInfo revision, final String entityTypeName) {
if (identifier == null || identifier.trim().isEmpty()) {
throw new IllegalArgumentException(entityTypeName + " identifier is required");
}
// skip checking revision if feature is disabled
if (!revisionFeature.isEnabled()) {
return;
}
if (revision == null || revision.getVersion() == null) {
throw new IllegalArgumentException("Revision info must be specified.");
}
}
private <T extends RevisableEntity> T createRevisableEntity(final T requestEntity, final String entityTypeName,
final String creatorIdentity, final Supplier<T> createEntity) {
// skip using the entity service if revision feature is disabled
if (!revisionFeature.isEnabled()) {
return createEntity.get();
} else {
try {
return entityService.create(requestEntity, creatorIdentity, createEntity);
} catch (InvalidRevisionException e) {
final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "create", requestEntity.getIdentifier());
throw new InvalidRevisionException(msg, e);
}
}
}
private <T extends RevisableEntity> T updateRevisableEntity(final T requestEntity, final String entityTypeName,
final String updaterIdentity, final Supplier<T> updateEntity) {
// skip using the entity service if revision feature is disabled
if (!revisionFeature.isEnabled()) {
return updateEntity.get();
} else {
try {
return entityService.update(requestEntity, updaterIdentity, updateEntity);
} catch (InvalidRevisionException e) {
final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "update", requestEntity.getIdentifier());
throw new InvalidRevisionException(msg, e);
}
}
}
private <T extends RevisableEntity> T deleteRevisableEntity(final String entityIdentifier, final String entityTypeName,
final RevisionInfo revisionInfo, final Supplier<T> deleteEntity) {
// skip using the entity service if revision feature is disabled
if (!revisionFeature.isEnabled()) {
return deleteEntity.get();
} else {
try {
return entityService.delete(entityIdentifier, revisionInfo, deleteEntity);
} catch (InvalidRevisionException e) {
final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "delete", entityIdentifier);
throw new InvalidRevisionException(msg, e);
}
}
}
}