NIFIREG-358 Refactoring proxy authorization to be part of Authorizables
NIFIREG-358 Catching UntrustedProxyException when asking for authorized resources since it would be considered unauthorized
This closes #258.
Signed-off-by: Kevin Doran <kdoran@apache.org>
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
index 959e29e..f69ac3c 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
@@ -248,12 +248,8 @@
try (final ExtensionCloseable extClosable = ExtensionCloseable.withClassLoader(authorizerClassLoader)) {
authorizer.onConfigured(authorizerConfigurationContext);
}
-
- // wrap the integrity checked Authorizer with the FrameworkAuthorizer
- authorizer = createFrameworkAuthorizer(authorizer);
}
-
} catch (AuthorizerFactoryException e) {
throw e;
} catch (Exception e) {
@@ -427,14 +423,6 @@
return instance;
}
- private Authorizer createFrameworkAuthorizer(final Authorizer baseAuthorizer) {
- if (baseAuthorizer instanceof ManagedAuthorizer) {
- return new FrameworkManagedAuthorizer((ManagedAuthorizer) baseAuthorizer, registryService);
- } else {
- return new FrameworkAuthorizer(baseAuthorizer, registryService);
- }
- }
-
private void performMethodInjection(final Object instance, final Class authorizerClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (final Method method : authorizerClass.getMethods()) {
if (method.isAnnotationPresent(AuthorizerContext.class)) {
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java
deleted file mode 100644
index 08fb8f0..0000000
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.registry.security.authorization;
-
-import org.apache.nifi.registry.bucket.Bucket;
-import org.apache.nifi.registry.exception.ResourceNotFoundException;
-import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
-import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
-import org.apache.nifi.registry.security.authorization.resource.ResourceType;
-import org.apache.nifi.registry.security.authorization.user.NiFiUser;
-import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
-import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
-import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
-import org.apache.nifi.registry.service.RegistryService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Wraps an Authorizer and adds framework level logic for authorizing proxies, public resources, and anything else
- * that needs to be done on top of the regular Authorizer.
- */
-public class FrameworkAuthorizer implements Authorizer {
-
- public static Logger LOGGER = LoggerFactory.getLogger(FrameworkAuthorizer.class);
-
- private static final Authorizable PROXY_AUTHORIZABLE = new Authorizable() {
- @Override
- public Authorizable getParentAuthorizable() {
- return null;
- }
-
- @Override
- public Resource getResource() {
- return ResourceFactory.getProxyResource();
- }
- };
-
- private final Authorizer wrappedAuthorizer;
- private final RegistryService registryService;
-
- public FrameworkAuthorizer(final Authorizer wrappedAuthorizer, final RegistryService registryService) {
- this.wrappedAuthorizer = Objects.requireNonNull(wrappedAuthorizer);
- this.registryService = Objects.requireNonNull(registryService);
- }
-
- @Override
- public void initialize(final AuthorizerInitializationContext initializationContext) throws SecurityProviderCreationException {
- wrappedAuthorizer.initialize(initializationContext);
- }
-
- @Override
- public void onConfigured(final AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
- wrappedAuthorizer.onConfigured(configurationContext);
- }
-
- @Override
- public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException {
- final Resource resource = request.getResource();
- final RequestAction requestAction = request.getAction();
-
- /**
- * If the request is for a resource that has been made public and action is READ, then it should automatically be authorized.
- *
- * This needs to be checked before the proxy authorizations b/c access to a public resource should always be allowed.
- */
-
- final boolean allowPublicAccess = isPublicAccessAllowed(resource, requestAction);
- if (allowPublicAccess) {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Authorizing access to public resource '{}'", new Object[]{resource.getIdentifier()});
- }
- return AuthorizationResult.approved();
- }
-
- /**
- * Deny an anonymous user access to anything else, they should only have access to publicly readable resources checked above
- */
-
- if (request.isAnonymous()) {
- return AuthorizationResult.denied("Anonymous access is not authorized");
- }
-
- /*
- * If the request has a proxy chain, ensure each identity in the chain is an authorized proxy for the given action.
- *
- * The action comes from the original request. For example, if user1 is proxied by proxy1, and it is a WRITE request
- * to /buckets/12345, then we need to determine if proxy1 is authorized to proxy WRITE requests.
- */
-
- final List<String> proxyChainIdentities = request.getProxyIdentities();
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Found {} proxy identities", new Object[]{proxyChainIdentities.size()});
- }
-
- for (final String proxyIdentity : proxyChainIdentities) {
- final NiFiUser proxyNiFiUser = createProxyNiFiUser(proxyIdentity);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Authorizing proxy [{}] for {}", new Object[]{proxyIdentity, requestAction});
- }
-
- try {
- PROXY_AUTHORIZABLE.authorize(wrappedAuthorizer, requestAction, proxyNiFiUser);
- } catch (final AccessDeniedException e) {
- final String actionString = requestAction.toString();
- return AuthorizationResult.denied(String.format("Untrusted proxy [%s] for %s operation.", proxyIdentity, actionString));
- }
- }
-
- /**
- * All other authorization decisions need to be delegated to the original wrapped Authorizer.
- */
-
- return wrappedAuthorizer.authorize(request);
- }
-
- /**
- * Determines if the given Resource is considered public for the action being performed.
- *
- * @param resource a Resource being authorized
- * @param action the action being performed
- * @return true if the resource is public for the given action, false otherwise
- */
- private boolean isPublicAccessAllowed(final Resource resource, final RequestAction action) {
- if (resource == null || action == null) {
- return false;
- }
-
- final String resourceIdentifier = resource.getIdentifier();
- if (resourceIdentifier == null || !resourceIdentifier.startsWith(ResourceType.Bucket.getValue() + "/")) {
- return false;
- }
-
- final int lastSlashIndex = resourceIdentifier.lastIndexOf("/");
- if (lastSlashIndex < 0 || lastSlashIndex >= resourceIdentifier.length() - 1) {
- return false;
- }
-
- final String bucketId = resourceIdentifier.substring(lastSlashIndex + 1);
- try {
- final Bucket bucket = registryService.getBucket(bucketId);
- return bucket.isAllowPublicRead() && action == RequestAction.READ;
- } catch (ResourceNotFoundException rnfe) {
- // if not found then we can't determine public access, so return false to delegate to regular authorizer
- LOGGER.debug("Cannot determine public access, bucket not found with id [{}]", new Object[]{bucketId});
- return false;
- } catch (Exception e) {
- LOGGER.error("Error checking public access to bucket with id [{}]", new Object[]{bucketId}, e);
- return false;
- }
- }
-
- /**
- * Creates a NiFiUser for the given proxy identity.
- *
- * This is only intended to be used for authorizing the given proxy identity against the /proxy resource, so we
- * don't need to populate the rest of the info on this user.
- *
- * @param proxyIdentity the proxy identity
- * @return the NiFiUser
- */
- private NiFiUser createProxyNiFiUser(final String proxyIdentity) {
- return new StandardNiFiUser.Builder().identity(proxyIdentity).build();
- }
-
- @Override
- public void preDestruction() throws SecurityProviderDestructionException {
- wrappedAuthorizer.preDestruction();
- }
-
-}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java
deleted file mode 100644
index 478482e..0000000
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.registry.security.authorization;
-
-import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
-import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
-import org.apache.nifi.registry.service.RegistryService;
-
-/**
- * Similar to FrameworkAuthorizer, but specifically for wrapping a ManagedAuthorizer.
- */
-public class FrameworkManagedAuthorizer extends FrameworkAuthorizer implements ManagedAuthorizer {
-
- private final ManagedAuthorizer wrappedManagedAuthorizer;
-
- public FrameworkManagedAuthorizer(final ManagedAuthorizer wrappedManagedAuthorizer, final RegistryService registryService) {
- super(wrappedManagedAuthorizer, registryService);
- this.wrappedManagedAuthorizer = wrappedManagedAuthorizer;
- }
-
- @Override
- public String getFingerprint() throws AuthorizationAccessException {
- return wrappedManagedAuthorizer.getFingerprint();
- }
-
- @Override
- public void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException {
- wrappedManagedAuthorizer.inheritFingerprint(fingerprint);
- }
-
- @Override
- public void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
- wrappedManagedAuthorizer.checkInheritability(proposedFingerprint);
- }
-
- @Override
- public AccessPolicyProvider getAccessPolicyProvider() {
- return wrappedManagedAuthorizer.getAccessPolicyProvider();
- }
-}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
index 18c2a52..6f68ebe 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
@@ -17,15 +17,22 @@
package org.apache.nifi.registry.security.authorization;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.security.authorization.resource.Authorizable;
import org.apache.nifi.registry.security.authorization.resource.InheritingAuthorizable;
+import org.apache.nifi.registry.security.authorization.resource.ProxyChainAuthorizable;
+import org.apache.nifi.registry.security.authorization.resource.PublicCheckingAuthorizable;
import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.apache.nifi.registry.service.RegistryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import java.util.Objects;
+
@Component
public class StandardAuthorizableLookup implements AuthorizableLookup {
@@ -103,14 +110,21 @@
}
};
+ private final RegistryService registryService;
+
+ @Autowired
+ public StandardAuthorizableLookup(final RegistryService registryService) {
+ this.registryService = Objects.requireNonNull(registryService);
+ }
+
@Override
public Authorizable getActuatorAuthorizable() {
- return ACTUATOR_AUTHORIZABLE;
+ return new ProxyChainAuthorizable(ACTUATOR_AUTHORIZABLE, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
public Authorizable getSwaggerAuthorizable() {
- return SWAGGER_AUTHORIZABLE;
+ return new ProxyChainAuthorizable(SWAGGER_AUTHORIZABLE, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
@@ -120,34 +134,42 @@
@Override
public Authorizable getTenantsAuthorizable() {
- return TENANTS_AUTHORIZABLE;
+ return new ProxyChainAuthorizable(TENANTS_AUTHORIZABLE, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
public Authorizable getPoliciesAuthorizable() {
- return POLICIES_AUTHORIZABLE;
+ return new ProxyChainAuthorizable(POLICIES_AUTHORIZABLE, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
public Authorizable getBucketsAuthorizable() {
- return BUCKETS_AUTHORIZABLE;
+ return new ProxyChainAuthorizable(BUCKETS_AUTHORIZABLE, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
public Authorizable getBucketAuthorizable(String bucketIdentifier) {
- // Note - this returns a special Authorizable type that inherits permissions from the parent Authorizable
- return new InheritingAuthorizable() {
+ // Note - this creates a special Authorizable type that inherits permissions from the parent Authorizable
+ final Authorizable inheritingAuthorizable = new InheritingAuthorizable() {
@Override
public Authorizable getParentAuthorizable() {
- return getBucketsAuthorizable();
+ // Use the unwrapped buckets authorizable here so that we don't reauthorize the proxy chain
+ return BUCKETS_AUTHORIZABLE;
}
@Override
public Resource getResource() {
return ResourceFactory.getBucketResource(bucketIdentifier, "Bucket with ID " + bucketIdentifier);
}
+
};
+
+ // Wrap the inheriting Authorizable with logic that first checks if public access is allowed, if not then delegates to the inheriting Authorizable
+ final Authorizable publicCheckingAuthorizable = new PublicCheckingAuthorizable(inheritingAuthorizable, this::isPublicAccessAllowed);
+
+ // Return ProxyChainAuthorizable -> public checking Authorizable -> inheriting Authorizable
+ return new ProxyChainAuthorizable(publicCheckingAuthorizable, PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
}
@Override
@@ -217,4 +239,44 @@
return authorizable;
}
+ /**
+ * Determines if the given Resource is considered public for the action being performed.
+ *
+ * @param resource a Resource being authorized
+ * @param action the action being performed
+ * @return true if the resource is public for the given action, false otherwise
+ */
+ private boolean isPublicAccessAllowed(final Resource resource, final RequestAction action) {
+ if (resource == null || action == null) {
+ return false;
+ }
+
+ if (action != RequestAction.READ) {
+ return false;
+ }
+
+ final String resourceIdentifier = resource.getIdentifier();
+ if (resourceIdentifier == null || !resourceIdentifier.startsWith(ResourceType.Bucket.getValue() + "/")) {
+ return false;
+ }
+
+ final int lastSlashIndex = resourceIdentifier.lastIndexOf("/");
+ if (lastSlashIndex < 0 || lastSlashIndex >= resourceIdentifier.length() - 1) {
+ return false;
+ }
+
+ final String bucketId = resourceIdentifier.substring(lastSlashIndex + 1);
+ try {
+ final Bucket bucket = registryService.getBucket(bucketId);
+ return bucket.isAllowPublicRead();
+ } catch (ResourceNotFoundException rnfe) {
+ // if not found then we can't determine public access, so return false to delegate to regular authorizer
+ logger.debug("Cannot determine public access, bucket not found with id [{}]", new Object[]{bucketId});
+ return false;
+ } catch (Exception e) {
+ logger.error("Error checking public access to bucket with id [{}]", new Object[]{bucketId}, e);
+ return false;
+ }
+ }
+
}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java
new file mode 100644
index 0000000..fbf1580
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.security.authorization;
+
+public class UntrustedProxyException extends RuntimeException {
+
+ public UntrustedProxyException(String message) {
+ super(message);
+ }
+
+ public UntrustedProxyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
index 04cb469..c461965 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
@@ -27,9 +27,7 @@
import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
import org.apache.nifi.registry.security.authorization.user.NiFiUser;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
public interface Authorizable {
@@ -95,10 +93,6 @@
userContext = null;
}
- // Note: We don't include the proxy identities here since this is not a direct attempt to access the resource and
- // we just want to determine if the end user is authorized. The proxy identities will be authorized when calling
- // Authorizable.authorize() during a direct access attempt for a resource.
-
final Resource resource = getResource();
final Resource requestedResource = getRequestedResource();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
@@ -211,18 +205,10 @@
userContext = null;
}
- final List<String> proxyChain = new ArrayList<>();
- NiFiUser proxyUser = user.getChain();
- while (proxyUser != null) {
- proxyChain.add(proxyUser.getIdentity());
- proxyUser = proxyUser.getChain();
- }
-
final Resource resource = getResource();
final Resource requestedResource = getRequestedResource();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.identity(user.getIdentity())
- .proxyIdentities(proxyChain)
.groups(user.getGroups())
.anonymous(user.isAnonymous())
.accessAttempt(true)
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java
new file mode 100644
index 0000000..aec8d76
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java
@@ -0,0 +1,145 @@
+/*
+ * 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.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.Resource;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+/**
+ * Authorizable that wraps another Authorizable and applies logic for authorizing the proxy chain, unless the resource
+ * allows public access, which then skips authorizing the proxy chain.
+ */
+public class ProxyChainAuthorizable implements Authorizable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProxyChainAuthorizable.class);
+
+ private final Authorizable wrappedAuthorizable;
+ private final Authorizable proxyAuthorizable;
+ private final BiFunction<Resource,RequestAction,Boolean> publicResourceCheck;
+
+ public ProxyChainAuthorizable(final Authorizable wrappedAuthorizable,
+ final Authorizable proxyAuthorizable,
+ final BiFunction<Resource,RequestAction,Boolean> publicResourceCheck) {
+ this.wrappedAuthorizable = Objects.requireNonNull(wrappedAuthorizable);
+ this.proxyAuthorizable = Objects.requireNonNull(proxyAuthorizable);
+ this.publicResourceCheck = Objects.requireNonNull(publicResourceCheck);
+ }
+
+ @Override
+ public Authorizable getParentAuthorizable() {
+ if (wrappedAuthorizable.getParentAuthorizable() == null) {
+ return null;
+ } else {
+ final Authorizable parentAuthorizable = wrappedAuthorizable.getParentAuthorizable();
+ return new ProxyChainAuthorizable(parentAuthorizable, proxyAuthorizable, publicResourceCheck);
+ }
+ }
+
+ @Override
+ public Resource getResource() {
+ return wrappedAuthorizable.getResource();
+ }
+
+ @Override
+ public AuthorizationResult checkAuthorization(final Authorizer authorizer, final RequestAction action, final NiFiUser user,
+ final Map<String, String> resourceContext) {
+ final Resource requestResource = wrappedAuthorizable.getRequestedResource();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Requested resource is {}", new Object[]{requestResource.getIdentifier()});
+ }
+
+ // if public access is allowed then we want to skip proxy authorization so just return
+ final Boolean isPublicAccessAllowed = publicResourceCheck.apply(requestResource, action);
+ if (isPublicAccessAllowed) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Proxy chain will not be checked, public access is allowed for {} on {}",
+ new Object[]{action.toString(), requestResource.getIdentifier()});
+ }
+ return AuthorizationResult.approved();
+ }
+
+ // otherwise public access is not allowed so check the proxy chain for the given action
+ NiFiUser proxyUser = user.getChain();
+ while (proxyUser != null) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Checking proxy [{}] for {}", new Object[]{proxyUser, action});
+ }
+
+ // if the proxy is denied then break out of the loop and return a denied result
+ final AuthorizationResult proxyAuthorizationResult = proxyAuthorizable.checkAuthorization(authorizer, action, proxyUser);
+ if (proxyAuthorizationResult.getResult() == AuthorizationResult.Result.Denied) {
+ final String deniedMessage = String.format("Untrusted proxy [%s] for %s operation.", proxyUser.getIdentity(), action.toString());
+ return AuthorizationResult.denied(deniedMessage);
+ }
+
+ proxyUser = proxyUser.getChain();
+ }
+
+ // at this point the proxy chain was approved so continue to check the original Authorizable
+ return wrappedAuthorizable.checkAuthorization(authorizer, action, user, resourceContext);
+ }
+
+ @Override
+ public void authorize(final Authorizer authorizer, final RequestAction action, final NiFiUser user,
+ final Map<String, String> resourceContext) throws AccessDeniedException {
+ final Resource requestResource = wrappedAuthorizable.getRequestedResource();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Requested resource is {}", new Object[]{requestResource.getIdentifier()});
+ }
+
+ // if public access is allowed then we want to skip proxy authorization so just return
+ final Boolean isPublicAccessAllowed = publicResourceCheck.apply(requestResource, action);
+ if (isPublicAccessAllowed) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Proxy chain will not be authorized, public access is allowed for {} on {}",
+ new Object[]{action.toString(), requestResource.getIdentifier()});
+ }
+ return;
+ }
+
+ // otherwise public access is not allowed so authorize proxy chain for the given action
+ NiFiUser proxyUser = user.getChain();
+ while (proxyUser != null) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Authorizing proxy [{}] for {}", new Object[]{proxyUser, action});
+ }
+
+ try {
+ proxyAuthorizable.authorize(authorizer, action, proxyUser);
+ } catch (final AccessDeniedException e) {
+ final String actionString = action.toString();
+ throw new UntrustedProxyException(String.format("Untrusted proxy [%s] for %s operation.", proxyUser.getIdentity(), actionString));
+ }
+ proxyUser = proxyUser.getChain();
+ }
+
+ // at this point the proxy chain was authorized so continue to authorize the original Authorizable
+ wrappedAuthorizable.authorize(authorizer, action, user, resourceContext);
+ }
+
+}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java
new file mode 100644
index 0000000..7cacb59
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java
@@ -0,0 +1,107 @@
+/*
+ * 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.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.Resource;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+/**
+ * Authorizable that first checks if public access is allowed for the resource and action. If it is then it short-circuits
+ * and returns approved, otherwise it continues and delegates to the wrapped Authorizable.
+ */
+public class PublicCheckingAuthorizable implements Authorizable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PublicCheckingAuthorizable.class);
+
+ private final Authorizable wrappedAuthorizable;
+ private final BiFunction<Resource, RequestAction,Boolean> publicResourceCheck;
+
+ public PublicCheckingAuthorizable(final Authorizable wrappedAuthorizable,
+ final BiFunction<Resource,RequestAction,Boolean> publicResourceCheck) {
+ this.wrappedAuthorizable = Objects.requireNonNull(wrappedAuthorizable);
+ this.publicResourceCheck = Objects.requireNonNull(publicResourceCheck);
+ }
+
+ @Override
+ public Authorizable getParentAuthorizable() {
+ return wrappedAuthorizable.getParentAuthorizable();
+ }
+
+ @Override
+ public Resource getResource() {
+ return wrappedAuthorizable.getResource();
+ }
+
+ @Override
+ public AuthorizationResult checkAuthorization(final Authorizer authorizer, final RequestAction action, final NiFiUser user,
+ final Map<String, String> resourceContext) {
+ final Resource resource = getResource();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Requested resource is {}", new Object[]{resource.getIdentifier()});
+ }
+
+ // if public access is allowed then return approved
+ final Boolean isPublicAccessAllowed = publicResourceCheck.apply(resource, action);
+ if(isPublicAccessAllowed) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Public access is allowed for {}", new Object[]{resource.getIdentifier()});
+ }
+ return AuthorizationResult.approved();
+ }
+
+ // otherwise delegate to the original inheriting authorizable
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Delegating to inheriting authorizable for {}", new Object[]{resource.getIdentifier()});
+ }
+ return wrappedAuthorizable.checkAuthorization(authorizer, action, user, resourceContext);
+ }
+
+ @Override
+ public void authorize(final Authorizer authorizer, final RequestAction action, final NiFiUser user,
+ final Map<String, String> resourceContext) throws AccessDeniedException {
+ final Resource resource = getResource();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Requested resource is {}", new Object[]{resource.getIdentifier()});
+ }
+
+ // if public access is allowed then skip authorization and return
+ final Boolean isPublicAccessAllowed = publicResourceCheck.apply(resource, action);
+ if(isPublicAccessAllowed) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Public access is allowed for {}", new Object[]{resource.getIdentifier()});
+ }
+ return;
+ }
+
+ // otherwise delegate to the original authorizable
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Delegating to inheriting authorizable for {}", new Object[]{resource.getIdentifier()});
+ }
+
+ wrappedAuthorizable.authorize(authorizer, action, user, resourceContext);
+ }
+}
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 12f39b6..503f27c 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -39,6 +39,7 @@
import org.apache.nifi.registry.security.authorization.Group;
import org.apache.nifi.registry.security.authorization.ManagedAuthorizer;
import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
import org.apache.nifi.registry.security.authorization.UserAndGroups;
import org.apache.nifi.registry.security.authorization.UserGroupProvider;
import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext;
@@ -487,7 +488,7 @@
.getAuthorizableByResource(resource.getIdentifier())
.authorize(authorizer, actionType, NiFiUserUtils.getNiFiUser());
return true;
- } catch (AccessDeniedException e) {
+ } catch (AccessDeniedException | UntrustedProxyException e) {
return false;
}
})
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
index 33b9f40..8035bd8 100644
--- a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
+++ b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -41,8 +41,7 @@
def setup() {
accessPolicyProvider.getUserGroupProvider() >> userGroupProvider
def standardAuthorizer = new StandardManagedAuthorizer(accessPolicyProvider, userGroupProvider)
- def frameworkAuthorizer = new FrameworkManagedAuthorizer(standardAuthorizer, registryService)
- authorizationService = new AuthorizationService(authorizableLookup, frameworkAuthorizer, registryService)
+ authorizationService = new AuthorizationService(authorizableLookup, standardAuthorizer, registryService)
}
// ----- User tests -------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java
deleted file mode 100644
index 2cc03f8..0000000
--- a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.registry.security.authorization;
-
-import org.apache.nifi.registry.bucket.Bucket;
-import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
-import org.apache.nifi.registry.service.RegistryService;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentMatcher;
-
-import java.util.Arrays;
-import java.util.UUID;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class TestFrameworkAuthorizer {
-
- private Authorizer frameworkAuthorizer;
- private Authorizer wrappedAuthorizer;
- private RegistryService registryService;
-
- private Bucket bucketPublic;
- private Bucket bucketNotPublic;
-
- @Before
- public void setup() {
- wrappedAuthorizer = mock(Authorizer.class);
- registryService = mock(RegistryService.class);
- frameworkAuthorizer = new FrameworkAuthorizer(wrappedAuthorizer, registryService);
-
- bucketPublic = new Bucket();
- bucketPublic.setIdentifier(UUID.randomUUID().toString());
- bucketPublic.setName("Public Bucket");
- bucketPublic.setAllowPublicRead(true);
-
- bucketNotPublic = new Bucket();
- bucketNotPublic.setIdentifier(UUID.randomUUID().toString());
- bucketNotPublic.setName("Non Public Bucket");
- bucketNotPublic.setAllowPublicRead(false);
-
- when(registryService.getBucket(bucketPublic.getIdentifier())).thenReturn(bucketPublic);
- when(registryService.getBucket(bucketNotPublic.getIdentifier())).thenReturn(bucketNotPublic);
- }
-
- @Test
- public void testReadPublicBucketWhenAnonymous() {
- final Resource resource = ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), bucketPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("anonymous")
- .anonymous(true)
- .build();
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
- // should never make it to wrapped authorizer
- verify(wrappedAuthorizer, times(0)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testReadNonPublicBucketWhenAnonymous() {
- final Resource resource = ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), bucketNotPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("anonymous")
- .anonymous(true)
- .build();
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
- // should be denied before making it to the wrapped authorizer since the user is anonymous
- verify(wrappedAuthorizer, times(0)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testWritePublicBucketWhenAnonymous() {
- final Resource resource = ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), bucketPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.WRITE)
- .accessAttempt(true)
- .identity("anonymous")
- .anonymous(true)
- .build();
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
- // should be denied before making it to wrapped authorizer since request is anonymous
- verify(wrappedAuthorizer, times(0)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testReadPublicBucketWhenNotAnonymous() {
- final Resource resource = ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), bucketPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("user1")
- .anonymous(false)
- .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
- .build();
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
- // should never make it to wrapped authorizer
- verify(wrappedAuthorizer, times(0)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testReadNonPublicBucketWhenNotAnonymousAndAuthorizedProxies() {
- final Resource resource = ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), bucketNotPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("user1")
- .anonymous(false)
- .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
- .build();
-
- // since the bucket is not public it will fall through to the wrapped authorizer
- when(wrappedAuthorizer.authorize(any(AuthorizationRequest.class)))
- .thenReturn(AuthorizationResult.approved());
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
- // should make 3 calls to the wrapped authorizer to authorize user1, proxy1, proxy2
- verify(wrappedAuthorizer, times(3)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testReadNonPublicBucketWhenNotAnonymousAndUnauthorizedProxy() {
- final Resource resource = ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), bucketNotPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("user1")
- .anonymous(false)
- .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
- .build();
-
- // since the bucket is not public and the user is not anonymous, it will continue to proxy authorization
-
- // simulate the first proxy being authorized for READ actions
- final AuthorizationRequestMatcher proxy1Matcher = new AuthorizationRequestMatcher(
- "proxy1", ResourceFactory.getProxyResource(), request.getAction());
- when(wrappedAuthorizer.authorize(argThat(proxy1Matcher))).thenReturn(AuthorizationResult.approved());
-
- // simulate the second proxy being unauthorized for READ actions
- final AuthorizationRequestMatcher proxy2Matcher = new AuthorizationRequestMatcher(
- "proxy2", ResourceFactory.getProxyResource(), request.getAction());
- when(wrappedAuthorizer.authorize(argThat(proxy2Matcher))).thenReturn(AuthorizationResult.denied("denied"));
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
- // should make 2 calls to the wrapped authorizer for the two proxies
- verify(wrappedAuthorizer, times(2)).authorize(any(AuthorizationRequest.class));
- }
-
- @Test
- public void testReadNonPublicBucketWhenNotAnonymousAndUnauthorizedEndUser() {
- final Resource resource = ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), bucketNotPublic.getName());
-
- final AuthorizationRequest request = new AuthorizationRequest.Builder()
- .resource(resource)
- .requestedResource(resource)
- .action(RequestAction.READ)
- .accessAttempt(true)
- .identity("user1")
- .anonymous(false)
- .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
- .build();
-
- // since the bucket is not public and the user is not anonymous, it will continue to proxy authorization
-
- // simulate the first proxy being authorized for READ actions
- final AuthorizationRequestMatcher proxy1Matcher = new AuthorizationRequestMatcher(
- "proxy1", ResourceFactory.getProxyResource(), request.getAction());
- when(wrappedAuthorizer.authorize(argThat(proxy1Matcher))).thenReturn(AuthorizationResult.approved());
-
- // simulate the second proxy being authorized for READ actions
- final AuthorizationRequestMatcher proxy2Matcher = new AuthorizationRequestMatcher(
- "proxy2", ResourceFactory.getProxyResource(), request.getAction());
- when(wrappedAuthorizer.authorize(argThat(proxy2Matcher))).thenReturn(AuthorizationResult.approved());
-
- // simulate the end user being unauthorized for READ actions
- final AuthorizationRequestMatcher user1Matcher = new AuthorizationRequestMatcher(
- "user1", resource, request.getAction());
- when(wrappedAuthorizer.authorize(argThat(user1Matcher))).thenReturn(AuthorizationResult.denied("denied"));
-
- final AuthorizationResult result = frameworkAuthorizer.authorize(request);
- assertNotNull(result);
- assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
- // should make 3 calls to the wrapped authorizer for the two proxies and end user
- verify(wrappedAuthorizer, times(3)).authorize(any(AuthorizationRequest.class));
- }
-
-
- /**
- * Matcher for matching Authorization requests.
- */
- private static class AuthorizationRequestMatcher implements ArgumentMatcher<AuthorizationRequest> {
-
- private final String identity;
- private final Resource resource;
- private final RequestAction action;
-
- public AuthorizationRequestMatcher(final String identity, final Resource resource, final RequestAction action) {
- this.identity = identity;
- this.resource = resource;
- this.action = action;
- }
-
- @Override
- public boolean matches(final AuthorizationRequest request) {
- if (request == null) {
- return false;
- }
-
- return identity.equals(request.getIdentity())
- && resource.getIdentifier().equals(request.getResource().getIdentifier())
- && action == request.getAction();
- }
- }
-}
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java
new file mode 100644
index 0000000..2804ac7
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java
@@ -0,0 +1,404 @@
+/*
+ * 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.security.authorization;
+
+import org.apache.nifi.registry.bucket.Bucket;
+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.ResourceFactory;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
+import org.apache.nifi.registry.service.RegistryService;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TestStandardAuthorizableLookup {
+
+ private static final NiFiUser USER_NO_PROXY_CHAIN = new StandardNiFiUser.Builder()
+ .identity("user1")
+ .build();
+
+ private static final NiFiUser USER_WITH_PROXY_CHAIN = new StandardNiFiUser.Builder()
+ .identity("user1")
+ .chain(new StandardNiFiUser.Builder().identity("CN=localhost, OU=NIFI").build())
+ .build();
+
+ private Authorizer authorizer;
+ private RegistryService registryService;
+ private AuthorizableLookup authorizableLookup;
+
+ private Bucket bucketPublic;
+ private Bucket bucketNotPublic;
+
+ @Before
+ public void setup() {
+ authorizer = mock(Authorizer.class);
+ registryService = mock(RegistryService.class);
+ authorizableLookup = new StandardAuthorizableLookup(registryService);
+
+ bucketPublic = new Bucket();
+ bucketPublic.setIdentifier(UUID.randomUUID().toString());
+ bucketPublic.setName("Public Bucket");
+ bucketPublic.setAllowPublicRead(true);
+
+ bucketNotPublic = new Bucket();
+ bucketNotPublic.setIdentifier(UUID.randomUUID().toString());
+ bucketNotPublic.setName("Non Public Bucket");
+ bucketNotPublic.setAllowPublicRead(false);
+
+ when(registryService.getBucket(bucketPublic.getIdentifier())).thenReturn(bucketPublic);
+ when(registryService.getBucket(bucketNotPublic.getIdentifier())).thenReturn(bucketNotPublic);
+ }
+
+ // Test check method for Bucket Authorizable
+
+ @Test
+ public void testCheckReadPublicBucketWithNoProxyChain() {
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, RequestAction.READ, USER_NO_PROXY_CHAIN);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+ // Should never call authorizer because resource is public
+ verify(authorizer, times(0)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testCheckReadPublicBucketWithProxyChain() {
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, RequestAction.READ, USER_WITH_PROXY_CHAIN);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+ // Should never call authorizer because resource is public
+ verify(authorizer, times(0)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testCheckWritePublicBucketWithUnauthorizedUserAndNoProxyChain() {
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, USER_NO_PROXY_CHAIN);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // second request will go to parent of /buckets
+ final AuthorizationRequest expectedBucketsAuthorizationRequest = getBucketsAuthorizationRequest(action, USER_NO_PROXY_CHAIN);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // should reach authorizer and return denied
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, action, USER_NO_PROXY_CHAIN);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+ // Should call authorizer twice for specific bucket and top-level /buckets
+ verify(authorizer, times(2)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testCheckWritePublicBucketWithUnauthorizedProxyChain() {
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, USER_WITH_PROXY_CHAIN.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // the authorization of the proxy chain should return denied
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, action, USER_WITH_PROXY_CHAIN);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+ // Should never call authorizer once for /proxy and then return denied
+ verify(authorizer, times(1)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testCheckWritePublicBucketWithUnauthorizedUserAndAuthorizedProxyChain() {
+ final NiFiUser user = USER_WITH_PROXY_CHAIN;
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, user.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // second request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // third request will go to parent of /buckets
+ final AuthorizationRequest expectedBucketsAuthorizationRequest = getBucketsAuthorizationRequest(action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // the authorization of the proxy chain should return denied
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, action, user);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+ // Should call authorizer three time for /proxy, /bucket/{id}, and /buckets
+ verify(authorizer, times(3)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testCheckWritePublicBucketWithAuthorizedUserAndAuthorizedProxyChain() {
+ final NiFiUser user = USER_WITH_PROXY_CHAIN;
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, user.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // second request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // the authorization should all return approved
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ final AuthorizationResult result = bucketAuthorizable.checkAuthorization(authorizer, action, user);
+ assertNotNull(result);
+ assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+ // Should call authorizer two times for /proxy and /bucket/{id}
+ verify(authorizer, times(2)).authorize(any(AuthorizationRequest.class));
+ }
+
+ // Test authorize method for Bucket Authorizable
+
+ @Test
+ public void testAuthorizeReadPublicBucketWithNoProxyChain() {
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ bucketAuthorizable.authorize(authorizer, RequestAction.READ, USER_NO_PROXY_CHAIN);
+
+ // Should never call authorizer because resource is public
+ verify(authorizer, times(0)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testAuthorizeReadPublicBucketWithProxyChain() {
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ bucketAuthorizable.authorize(authorizer, RequestAction.READ, USER_WITH_PROXY_CHAIN);
+
+ // Should never call authorizer because resource is public
+ verify(authorizer, times(0)).authorize(any(AuthorizationRequest.class));
+ }
+
+ @Test
+ public void testAuthorizeWritePublicBucketWithUnauthorizedUserAndNoProxyChain() {
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, USER_NO_PROXY_CHAIN);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // second request will go to parent of /buckets
+ final AuthorizationRequest expectedBucketsAuthorizationRequest = getBucketsAuthorizationRequest(action, USER_NO_PROXY_CHAIN);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // should reach authorizer and throw access denied
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ try {
+ bucketAuthorizable.authorize(authorizer, action, USER_NO_PROXY_CHAIN);
+ Assert.fail("Should have thrown exception");
+ } catch (AccessDeniedException e) {
+ // Should never call authorizer twice for specific bucket and top-level /buckets
+ verify(authorizer, times(2)).authorize(any(AuthorizationRequest.class));
+ }
+ }
+
+ @Test
+ public void testAuthorizeWritePublicBucketWithUnauthorizedProxyChain() {
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, USER_WITH_PROXY_CHAIN.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // the authorization of the proxy chain should throw UntrustedProxyException
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ try {
+ bucketAuthorizable.authorize(authorizer, action, USER_WITH_PROXY_CHAIN);
+ Assert.fail("Should have thrown exception");
+ } catch (UntrustedProxyException e) {
+ // Should call authorizer once for /proxy and then throw exception
+ verify(authorizer, times(1)).authorize(any(AuthorizationRequest.class));
+ }
+ }
+
+ @Test
+ public void testAuthorizeWritePublicBucketWithUnauthorizedUserAndAuthorizedProxyChain() {
+ final NiFiUser user = USER_WITH_PROXY_CHAIN;
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, user.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // second request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // third request will go to parent of /buckets
+ final AuthorizationRequest expectedBucketsAuthorizationRequest = getBucketsAuthorizationRequest(action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.denied());
+
+ // the authorization of the proxy chain should throw UntrustedProxyException
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ try {
+ bucketAuthorizable.authorize(authorizer, action, user);
+ Assert.fail("Should have thrown exception");
+ } catch (AccessDeniedException e) {
+ // Should call authorizer three times for /proxy, /bucket/{id}, and /buckets
+ verify(authorizer, times(3)).authorize(any(AuthorizationRequest.class));
+ }
+ }
+
+ @Test
+ public void testAuthorizeWritePublicBucketWithAuthorizedUserAndAuthorizedProxyChain() {
+ final NiFiUser user = USER_WITH_PROXY_CHAIN;
+ final RequestAction action = RequestAction.WRITE;
+
+ // first request will be to authorize the proxy
+ final AuthorizationRequest expectedProxyAuthorizationRequest = getProxyAuthorizationRequest(action, user.getChain());
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // second request will be to the specific bucket
+ final AuthorizationRequest expectedBucketAuthorizationRequest = getBucketAuthorizationRequest(
+ bucketPublic.getIdentifier(), action, user);
+
+ when(authorizer.authorize(argThat(new AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+ .thenReturn(AuthorizationResult.approved());
+
+ // the authorization should all return approved so no exception
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+ bucketAuthorizable.authorize(authorizer, action, user);
+
+ // Should call authorizer two times for /proxy and /bucket/{id}
+ verify(authorizer, times(2)).authorize(any(AuthorizationRequest.class));
+ }
+
+ private AuthorizationRequest getBucketAuthorizationRequest(final String bucketIdentifier, final RequestAction action, final NiFiUser user) {
+ return new AuthorizationRequest.Builder()
+ .resource(ResourceFactory.getBucketResource(bucketIdentifier, bucketIdentifier))
+ .action(action)
+ .identity(user.getIdentity())
+ .accessAttempt(true)
+ .anonymous(false)
+ .build();
+ }
+
+ private AuthorizationRequest getBucketsAuthorizationRequest(final RequestAction action, final NiFiUser user) {
+ return new AuthorizationRequest.Builder()
+ .resource(ResourceFactory.getBucketsResource())
+ .action(action)
+ .identity(user.getIdentity())
+ .accessAttempt(true)
+ .anonymous(false)
+ .build();
+ }
+
+ private AuthorizationRequest getProxyAuthorizationRequest(final RequestAction action, final NiFiUser user) {
+ return new AuthorizationRequest.Builder()
+ .resource(ResourceFactory.getProxyResource())
+ .action(action)
+ .identity(user.getIdentity())
+ .accessAttempt(true)
+ .anonymous(false)
+ .build();
+ }
+
+ /**
+ * ArugmentMatcher for AuthorizationRequest.
+ */
+ private static class AuthorizationRequestMatcher implements ArgumentMatcher<AuthorizationRequest> {
+
+ private final AuthorizationRequest expectedAuthorizationRequest;
+
+ public AuthorizationRequestMatcher(final AuthorizationRequest expectedAuthorizationRequest) {
+ this.expectedAuthorizationRequest = expectedAuthorizationRequest;
+ }
+
+ @Override
+ public boolean matches(final AuthorizationRequest authorizationRequest) {
+ if (authorizationRequest == null) {
+ return false;
+ }
+
+ final String requestResourceId = authorizationRequest.getResource().getIdentifier();
+ final String expectedResourceId = expectedAuthorizationRequest.getResource().getIdentifier();
+
+ final String requestAction = authorizationRequest.getAction().toString();
+ final String expectedAction = expectedAuthorizationRequest.getAction().toString();
+
+ final String requestUserIdentity = authorizationRequest.getIdentity();
+ final String expectedUserIdentity = authorizationRequest.getIdentity();
+
+ return requestResourceId.equals(expectedResourceId)
+ && requestAction.equals(expectedAction)
+ && requestUserIdentity.equals(expectedUserIdentity);
+ }
+ }
+}
diff --git a/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java b/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
index 56b7b45..3e832fa 100644
--- a/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
+++ b/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
@@ -109,6 +109,8 @@
* The identities in the proxy chain for the request. Will be empty if the request was not proxied.
*
* @return The identities in the proxy chain
+ *
+ * @deprecated no longer populated
*/
public List<String> getProxyIdentities() {
return proxyIdentities;
@@ -210,6 +212,9 @@
return this;
}
+ /**
+ * @deprecated no longer populated by the framework
+ */
public Builder proxyIdentities(final List<String> proxyIdentities) {
this.proxyIdentities = proxyIdentities;
return this;
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java
new file mode 100644
index 0000000..453dbd5
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.web.mapper;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps an UntrustedProxyException to a FORBIDDEN response.
+ */
+@Component
+@Provider
+public class UntrustedProxyExceptionMapper implements ExceptionMapper<UntrustedProxyException> {
+
+ private static Logger LOGGER = LoggerFactory.getLogger(UntrustedProxyException.class);
+
+ @Override
+ public Response toResponse(final UntrustedProxyException exception) {
+ LOGGER.info("{}. Returning {} response.", exception, Response.Status.FORBIDDEN);
+ LOGGER.debug(StringUtils.EMPTY, exception);
+
+ return Response.status(Response.Status.FORBIDDEN)
+ .entity(exception.getMessage())
+ .type("text/plain")
+ .build();
+ }
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy b/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
index e27dfbe..806f73c 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
@@ -20,6 +20,7 @@
import org.apache.nifi.registry.security.authorization.resource.Authorizable
import org.apache.nifi.registry.security.authorization.resource.ResourceType
import org.apache.nifi.registry.service.AuthorizationService
+import org.apache.nifi.registry.service.RegistryService
import org.apache.nifi.registry.web.security.authorization.HttpMethodAuthorizationRules
import org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter
import org.apache.nifi.registry.web.security.authorization.StandardHttpMethodAuthorizationRules
@@ -34,7 +35,8 @@
class ResourceAuthorizationFilterSpec extends Specification {
- AuthorizableLookup authorizableLookup = new StandardAuthorizableLookup()
+ RegistryService registryService = Mock(RegistryService)
+ AuthorizableLookup authorizableLookup = new StandardAuthorizableLookup(registryService)
AuthorizationService mockAuthorizationService = Mock(AuthorizationService)
FilterChain mockFilterChain = Mock(FilterChain)
ResourceAuthorizationFilter.Builder resourceAuthorizationFilterBuilder