From 743c6b9c17e4176a4cad35f7898d8026052bfb85 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Wed, 28 Jun 2017 10:17:17 -0400 Subject: [PATCH] NIFI-4032: - Introducing the ManagedRangerAuthorizer. - Introducing the AuthorizationAuditor. - Updating authorization requests to utilize Authorizable where ever possible so allow for a singular place to audit resource not found as denied when the parent authorizable is null (no more inheritance). - Updating unit tests as appropriate. - Addressing issues with broken web-api integration tests. NIFI-4032: - Generating the appropriate fingerprint for the ManagedRangerAuthorizer based on whether the UserGroupProvider is configurable. - Adding unit tests. Signed-off-by: Yolanda M. Davis This closes #2019 --- .../authorization/AuthorizationAuditor.java | 30 ++ .../authorization/AuthorizationRequest.java | 24 + .../authorization/resource/Authorizable.java | 36 +- .../AccessPolicyProviderFactory.java | 167 +------ ...AccessPolicyProviderInvocationHandler.java | 54 +++ .../nifi/authorization/AuthorizerFactory.java | 142 ++---- .../authorization/AuthorizerFactoryBean.java | 8 +- .../AuthorizerInvocationHandler.java | 54 +++ .../UserGroupProviderFactory.java | 223 +-------- .../UserGroupProviderInvocationHandler.java | 45 ++ .../authorization/AuthorizerFactoryTest.java | 83 ++++ .../MockPolicyBasedAuthorizer.java | 17 +- .../authorization/AuthorizableLookup.java | 21 + .../StandardAuthorizableLookup.java | 51 ++ .../StandardNiFiWebConfigurationContext.java | 31 +- .../nifi/web/api/ControllerResource.java | 50 +- .../apache/nifi/web/api/CountersResource.java | 51 +- .../org/apache/nifi/web/api/FlowResource.java | 60 +-- .../nifi/web/api/ProvenanceResource.java | 38 +- .../apache/nifi/web/api/ResourceResource.java | 40 +- .../nifi/web/api/SiteToSiteResource.java | 39 +- .../web/api/SystemDiagnosticsResource.java | 40 +- .../nifi/integration/NiFiWebApiTest.java | 4 + .../ITConnectionAccessControl.java | 2 + .../security/NiFiAuthenticationProvider.java | 1 + .../x509/X509AuthenticationProvider.java | 42 +- .../ManagedRangerAuthorizer.java | 205 +++++++++ .../RangerBasePluginWithPolicies.java | 230 ++++++++- .../authorization/RangerNiFiAuthorizer.java | 42 +- .../org.apache.nifi.authorization.Authorizer | 1 + .../ManagedRangerAuthorizerTest.java | 200 ++++++++ .../TestRangerBasePluginWithPolicies.java | 435 +++++++++++++++++- .../TestRangerNiFiAuthorizer.java | 18 +- pom.xml | 2 +- 34 files changed, 1637 insertions(+), 849 deletions(-) create mode 100644 nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationAuditor.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInvocationHandler.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerInvocationHandler.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderInvocationHandler.java create mode 100644 nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java create mode 100644 nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationAuditor.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationAuditor.java new file mode 100644 index 0000000000..d4f4399f0f --- /dev/null +++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationAuditor.java @@ -0,0 +1,30 @@ +/* + * 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.authorization; + +public interface AuthorizationAuditor { + + /** + * Audits an authorization request. Will be invoked for any Approved or Denied results. ResourceNotFound + * will either re-attempt authorization using a parent resource or will generate a failure result and + * audit that. + * + * @param request the request for authorization + * @param result the authorization result + */ + void auditAccessAttempt(final AuthorizationRequest request, final AuthorizationResult result); +} diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java index d5b16007ba..6dba3bf28a 100644 --- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java +++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java @@ -31,6 +31,7 @@ public class AuthorizationRequest { public static final String DEFAULT_EXPLANATION = "Unable to perform the desired action."; private final Resource resource; + private final Resource requestedResource; private final String identity; private final Set groups; private final RequestAction action; @@ -64,6 +65,12 @@ public class AuthorizationRequest { return explanation; } }; + + if (builder.requestedResource == null) { + this.requestedResource = builder.resource; + } else { + this.requestedResource = builder.requestedResource; + } } /** @@ -75,6 +82,17 @@ public class AuthorizationRequest { return resource; } + /** + * The original Resource being requested. In cases with inherited policies, this will be a ancestor resource of + * of the current resource. The initial request, and cases without inheritance, the requested resource will be + * the same as the current resource. + * + * @return The requested resource + */ + public Resource getRequestedResource() { + return requestedResource; + } + /** * The identity accessing the Resource. May be null if the user could not authenticate. * @@ -154,6 +172,7 @@ public class AuthorizationRequest { public static final class Builder { private Resource resource; + private Resource requestedResource; private String identity; private Set groups; private Boolean isAnonymous; @@ -168,6 +187,11 @@ public class AuthorizationRequest { return this; } + public Builder requestedResource(final Resource requestedResource) { + this.requestedResource = requestedResource; + return this; + } + public Builder identity(final String identity) { this.identity = identity; return this; diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java index cd27e1061e..d632feb574 100644 --- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java +++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java @@ -17,6 +17,7 @@ package org.apache.nifi.authorization.resource; import org.apache.nifi.authorization.AccessDeniedException; +import org.apache.nifi.authorization.AuthorizationAuditor; import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.AuthorizationResult.Result; @@ -45,6 +46,17 @@ public interface Authorizable { */ Resource getResource(); + /** + * The originally requested resource for this Authorizable. Because policies are inherited, if a resource + * does not have a policy, this Authorizable may represent a parent resource and this method will return + * the originally requested resource. + * + * @return the originally requested resource + */ + default Resource getRequestedResource() { + return getResource(); + } + /** * Returns whether the current user is authorized for the specified action on the specified resource. This * method does not imply the user is directly attempting to access the specified resource. If the user is @@ -82,6 +94,7 @@ public interface Authorizable { } final Resource resource = getResource(); + final Resource requestedResource = getRequestedResource(); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity(user.getIdentity()) .groups(user.getGroups()) @@ -89,6 +102,7 @@ public interface Authorizable { .accessAttempt(false) .action(action) .resource(resource) + .requestedResource(requestedResource) .resourceContext(resourceContext) .userContext(userContext) .explanationSupplier(() -> { @@ -122,6 +136,11 @@ public interface Authorizable { return parent.getParentAuthorizable(); } + @Override + public Resource getRequestedResource() { + return requestedResource; + } + @Override public Resource getResource() { final Resource parentResource = parent.getResource(); @@ -187,6 +206,7 @@ public interface Authorizable { } final Resource resource = getResource(); + final Resource requestedResource = getRequestedResource(); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity(user.getIdentity()) .groups(user.getGroups()) @@ -194,6 +214,7 @@ public interface Authorizable { .accessAttempt(true) .action(action) .resource(resource) + .requestedResource(requestedResource) .resourceContext(resourceContext) .userContext(userContext) .explanationSupplier(() -> { @@ -215,7 +236,15 @@ public interface Authorizable { if (Result.ResourceNotFound.equals(result.getResult())) { final Authorizable parent = getParentAuthorizable(); if (parent == null) { - throw new AccessDeniedException("No applicable policies could be found."); + final AuthorizationResult failure = AuthorizationResult.denied("No applicable policies could be found."); + + // audit authorization request + if (authorizer instanceof AuthorizationAuditor) { + ((AuthorizationAuditor) authorizer).auditAccessAttempt(request, failure); + } + + // denied + throw new AccessDeniedException(failure.getExplanation()); } else { // create a custom authorizable to override the safe description but still defer to the parent authorizable final Authorizable parentProxy = new Authorizable() { @@ -224,6 +253,11 @@ public interface Authorizable { return parent.getParentAuthorizable(); } + @Override + public Resource getRequestedResource() { + return requestedResource; + } + @Override public Resource getResource() { final Resource parentResource = parent.getResource(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderFactory.java index 07ac168c2c..6d7f32bfa4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderFactory.java @@ -16,170 +16,21 @@ */ package org.apache.nifi.authorization; -import org.apache.nifi.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.authorization.exception.AuthorizerDestructionException; -import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.nar.NarCloseable; +import org.apache.commons.lang3.ClassUtils; -import java.util.Set; +import java.lang.reflect.Proxy; +import java.util.List; public final class AccessPolicyProviderFactory { - public static AccessPolicyProvider withNarLoader(final AccessPolicyProvider baseAccessPolicyProvider) { - if (baseAccessPolicyProvider instanceof ConfigurableAccessPolicyProvider) { - final ConfigurableAccessPolicyProvider baseConfigurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) baseAccessPolicyProvider; - return new ConfigurableAccessPolicyProvider() { - @Override - public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.addAccessPolicy(accessPolicy); - } - } + public static AccessPolicyProvider withNarLoader(final AccessPolicyProvider baseAccessPolicyProvider, final ClassLoader classLoader) { + final AccessPolicyProviderInvocationHandler invocationHandler = new AccessPolicyProviderInvocationHandler(baseAccessPolicyProvider, classLoader); - @Override - public boolean isConfigurable(AccessPolicy accessPolicy) { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.isConfigurable(accessPolicy); - } - } + // extract all interfaces... baseAccessPolicyProvider is non null so getAllInterfaces is non null + final List> interfaceList = ClassUtils.getAllInterfaces(baseAccessPolicyProvider.getClass()); + final Class[] interfaces = interfaceList.toArray(new Class[interfaceList.size()]); - @Override - public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.updateAccessPolicy(accessPolicy); - } - } - - @Override - public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.deleteAccessPolicy(accessPolicy); - } - } - - @Override - public Set getAccessPolicies() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.getAccessPolicies(); - } - } - - @Override - public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.getAccessPolicy(identifier); - } - } - - @Override - public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.getAccessPolicy(resourceIdentifier, action); - } - } - - @Override - public UserGroupProvider getUserGroupProvider() { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.getUserGroupProvider(); - } - } - - @Override - public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableAccessPolicyProvider.inheritFingerprint(fingerprint); - } - } - - @Override - public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableAccessPolicyProvider.checkInheritability(proposedFingerprint); - } - } - - @Override - public String getFingerprint() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableAccessPolicyProvider.getFingerprint(); - } - } - - @Override - public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableAccessPolicyProvider.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableAccessPolicyProvider.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableAccessPolicyProvider.preDestruction(); - } - } - }; - } else { - return new AccessPolicyProvider() { - @Override - public Set getAccessPolicies() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseAccessPolicyProvider.getAccessPolicies(); - } - } - - @Override - public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseAccessPolicyProvider.getAccessPolicy(identifier); - } - } - - @Override - public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseAccessPolicyProvider.getAccessPolicy(resourceIdentifier, action); - } - } - - @Override - public UserGroupProvider getUserGroupProvider() { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseAccessPolicyProvider.getUserGroupProvider(); - } - } - - @Override - public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseAccessPolicyProvider.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseAccessPolicyProvider.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseAccessPolicyProvider.preDestruction(); - } - } - }; - } + return (AccessPolicyProvider) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } private AccessPolicyProviderFactory() {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInvocationHandler.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInvocationHandler.java new file mode 100644 index 0000000000..e41afa3fa2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInvocationHandler.java @@ -0,0 +1,54 @@ +/* + * 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.authorization; + +import org.apache.nifi.nar.NarCloseable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class AccessPolicyProviderInvocationHandler implements InvocationHandler { + + private final AccessPolicyProvider accessPolicyProvider; + private final ClassLoader classLoader; + + public AccessPolicyProviderInvocationHandler(final AccessPolicyProvider accessPolicyProvider, final ClassLoader classLoader) { + this.accessPolicyProvider = accessPolicyProvider; + this.classLoader = classLoader; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { + if (AccessPolicyProvider.class.getMethod("getUserGroupProvider").equals(method)) { + final UserGroupProvider userGroupProvider = (UserGroupProvider) method.invoke(accessPolicyProvider, args); + if (userGroupProvider == null) { + return userGroupProvider; + } else { + return UserGroupProviderFactory.withNarLoader(userGroupProvider, classLoader); + } + } else { + return method.invoke(accessPolicyProvider, args); + } + } catch (final InvocationTargetException e) { + // If the proxied instance throws an Exception, it'll be wrapped in an InvocationTargetException. We want + // to instead re-throw what the proxied instance threw, so we pull it out of the InvocationTargetException. + throw e.getCause(); + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactory.java index 1573e0dbb5..f466ce7061 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactory.java @@ -16,12 +16,15 @@ */ package org.apache.nifi.authorization; +import org.apache.commons.lang3.ClassUtils; +import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.exception.AuthorizationAccessException; import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.apache.nifi.authorization.exception.AuthorizerDestructionException; import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.nar.NarCloseable; +import java.lang.reflect.Proxy; +import java.util.List; import java.util.Set; public final class AuthorizerFactory { @@ -68,6 +71,16 @@ public final class AuthorizerFactory { return false; } + private static void audit(final Authorizer authorizer, final AuthorizationRequest request, final AuthorizationResult result) { + // audit when... + // 1 - the authorizer supports auditing + // 2 - the request is an access attempt + // 3 - the result is either approved/denied, when resource is not found a subsequent request may be following with the parent resource + if (authorizer instanceof AuthorizationAuditor && request.isAccessAttempt() && !Result.ResourceNotFound.equals(result.getResult())) { + ((AuthorizationAuditor) authorizer).auditAccessAttempt(request, result); + } + } + public static Authorizer installIntegrityChecks(final Authorizer baseAuthorizer) { if (baseAuthorizer instanceof ManagedAuthorizer) { final ManagedAuthorizer baseManagedAuthorizer = (ManagedAuthorizer) baseAuthorizer; @@ -309,7 +322,12 @@ public final class AuthorizerFactory { @Override public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException { - return baseManagedAuthorizer.authorize(request); + final AuthorizationResult result = baseAuthorizer.authorize(request); + + // audit the authorization request + audit(baseAuthorizer, request, result); + + return result; } @Override @@ -352,7 +370,32 @@ public final class AuthorizerFactory { } }; } else { - return baseAuthorizer; + return new Authorizer() { + @Override + public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException { + final AuthorizationResult result = baseAuthorizer.authorize(request); + + // audit the authorization request + audit(baseAuthorizer, request, result); + + return result; + } + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + baseAuthorizer.initialize(initializationContext); + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + baseAuthorizer.onConfigured(configurationContext); + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + baseAuthorizer.preDestruction(); + } + }; } } @@ -363,96 +406,13 @@ public final class AuthorizerFactory { * @return authorizer */ public static Authorizer withNarLoader(final Authorizer baseAuthorizer, final ClassLoader classLoader) { - if (baseAuthorizer instanceof ManagedAuthorizer) { - final ManagedAuthorizer baseManagedAuthorizer = (ManagedAuthorizer) baseAuthorizer; - return new ManagedAuthorizer() { - @Override - public String getFingerprint() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - return baseManagedAuthorizer.getFingerprint(); - } - } + final AuthorizerInvocationHandler invocationHandler = new AuthorizerInvocationHandler(baseAuthorizer, classLoader); - @Override - public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseManagedAuthorizer.inheritFingerprint(fingerprint); - } - } + // extract all interfaces... baseAuthorizer is non null so getAllInterfaces is non null + final List> interfaceList = ClassUtils.getAllInterfaces(baseAuthorizer.getClass()); + final Class[] interfaces = interfaceList.toArray(new Class[interfaceList.size()]); - @Override - public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseManagedAuthorizer.checkInheritability(proposedFingerprint); - } - } - - @Override - public AccessPolicyProvider getAccessPolicyProvider() { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - return baseManagedAuthorizer.getAccessPolicyProvider(); - } - } - - @Override - public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - return baseManagedAuthorizer.authorize(request); - } - } - - @Override - public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseManagedAuthorizer.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseManagedAuthorizer.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseManagedAuthorizer.preDestruction(); - } - } - }; - } else { - return new Authorizer() { - @Override - public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - return baseAuthorizer.authorize(request); - } - } - - @Override - public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseAuthorizer.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseAuthorizer.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { - baseAuthorizer.preDestruction(); - } - } - }; - } + return (Authorizer) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } private AuthorizerFactory() {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java index 8ed5aaf0d4..fdc04a0c11 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java @@ -225,7 +225,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG } } - return UserGroupProviderFactory.withNarLoader(instance); + return UserGroupProviderFactory.withNarLoader(instance, userGroupProviderClassLoader); } private AccessPolicyProvider createAccessPolicyProvider(final String identifier, final String accessPolicyProviderClassName) throws Exception { @@ -273,7 +273,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG } } - return AccessPolicyProviderFactory.withNarLoader(instance); + return AccessPolicyProviderFactory.withNarLoader(instance, accessPolicyProviderClassLoader); } private Authorizer createAuthorizer(final String identifier, final String authorizerClassName, final String classpathResources) throws Exception { @@ -321,12 +321,12 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserG } } - if(StringUtils.isNotEmpty(classpathResources)) { + if (StringUtils.isNotEmpty(classpathResources)) { URL[] urls = ClassLoaderUtils.getURLsForClasspath(classpathResources, null, true); authorizerClassLoader = new URLClassLoader(urls, authorizerClassLoader); } - return AuthorizerFactory.installIntegrityChecks(AuthorizerFactory.withNarLoader(instance,authorizerClassLoader)); + return AuthorizerFactory.installIntegrityChecks(AuthorizerFactory.withNarLoader(instance, authorizerClassLoader)); } private AuthorizerConfigurationContext loadAuthorizerConfiguration(final String identifier, final List properties) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerInvocationHandler.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerInvocationHandler.java new file mode 100644 index 0000000000..228fe09312 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerInvocationHandler.java @@ -0,0 +1,54 @@ +/* + * 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.authorization; + +import org.apache.nifi.nar.NarCloseable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class AuthorizerInvocationHandler implements InvocationHandler { + + private final Authorizer authorizer; + private final ClassLoader classLoader; + + public AuthorizerInvocationHandler(final Authorizer authorizer, final ClassLoader classLoader) { + this.authorizer = authorizer; + this.classLoader = classLoader; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { + if (ManagedAuthorizer.class.getMethod("getAccessPolicyProvider").equals(method)) { + final AccessPolicyProvider accessPolicyProvider = (AccessPolicyProvider) method.invoke(authorizer, args); + if (accessPolicyProvider == null) { + return accessPolicyProvider; + } else { + return AccessPolicyProviderFactory.withNarLoader(accessPolicyProvider, classLoader); + } + } else { + return method.invoke(authorizer, args); + } + } catch (final InvocationTargetException e) { + // If the proxied instance throws an Exception, it'll be wrapped in an InvocationTargetException. We want + // to instead re-throw what the proxied instance threw, so we pull it out of the InvocationTargetException. + throw e.getCause(); + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderFactory.java index 5d1b02085e..2f09aa1d35 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderFactory.java @@ -16,226 +16,21 @@ */ package org.apache.nifi.authorization; -import org.apache.nifi.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.authorization.exception.AuthorizerDestructionException; -import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.nar.NarCloseable; +import org.apache.commons.lang3.ClassUtils; -import java.util.Set; +import java.lang.reflect.Proxy; +import java.util.List; public final class UserGroupProviderFactory { - public static UserGroupProvider withNarLoader(final UserGroupProvider baseUserGroupProvider) { - if (baseUserGroupProvider instanceof ConfigurableUserGroupProvider) { - final ConfigurableUserGroupProvider baseConfigurableUserGroupProvider = (ConfigurableUserGroupProvider) baseUserGroupProvider; - return new ConfigurableUserGroupProvider() { - @Override - public User addUser(User user) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.addUser(user); - } - } + public static UserGroupProvider withNarLoader(final UserGroupProvider baseUserGroupProvider, final ClassLoader classLoader) { + final UserGroupProviderInvocationHandler invocationHandler = new UserGroupProviderInvocationHandler(baseUserGroupProvider, classLoader); - @Override - public boolean isConfigurable(User user) { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.isConfigurable(user); - } - } + // extract all interfaces... baseUserGroupProvider is non null so getAllInterfaces is non null + final List> interfaceList = ClassUtils.getAllInterfaces(baseUserGroupProvider.getClass()); + final Class[] interfaces = interfaceList.toArray(new Class[interfaceList.size()]); - @Override - public User updateUser(User user) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.updateUser(user); - } - } - - @Override - public User deleteUser(User user) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.deleteUser(user); - } - } - - @Override - public Group addGroup(Group group) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.addGroup(group); - } - } - - @Override - public boolean isConfigurable(Group group) { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.isConfigurable(group); - } - } - - @Override - public Group updateGroup(Group group) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.updateGroup(group); - } - } - - @Override - public Group deleteGroup(Group group) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.deleteGroup(group); - } - } - - @Override - public Set getUsers() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getUsers(); - } - } - - @Override - public User getUser(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getUser(identifier); - } - } - - @Override - public User getUserByIdentity(String identity) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getUserByIdentity(identity); - } - } - - @Override - public Set getGroups() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getGroups(); - } - } - - @Override - public Group getGroup(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getGroup(identifier); - } - } - - @Override - public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getUserAndGroups(identity); - } - } - - @Override - public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableUserGroupProvider.inheritFingerprint(fingerprint); - } - } - - @Override - public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableUserGroupProvider.checkInheritability(proposedFingerprint); - } - } - - @Override - public String getFingerprint() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseConfigurableUserGroupProvider.getFingerprint(); - } - } - - @Override - public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableUserGroupProvider.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableUserGroupProvider.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseConfigurableUserGroupProvider.preDestruction(); - } - } - }; - } else { - return new UserGroupProvider() { - @Override - public Set getUsers() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getUsers(); - } - } - - @Override - public User getUser(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getUser(identifier); - } - } - - @Override - public User getUserByIdentity(String identity) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getUserByIdentity(identity); - } - } - - @Override - public Set getGroups() throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getGroups(); - } - } - - @Override - public Group getGroup(String identifier) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getGroup(identifier); - } - } - - @Override - public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - return baseUserGroupProvider.getUserAndGroups(identity); - } - } - - @Override - public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseUserGroupProvider.initialize(initializationContext); - } - } - - @Override - public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseUserGroupProvider.onConfigured(configurationContext); - } - } - - @Override - public void preDestruction() throws AuthorizerDestructionException { - try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { - baseUserGroupProvider.preDestruction(); - } - } - }; - } + return (UserGroupProvider) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } private UserGroupProviderFactory() {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderInvocationHandler.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderInvocationHandler.java new file mode 100644 index 0000000000..21feada44f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/UserGroupProviderInvocationHandler.java @@ -0,0 +1,45 @@ +/* + * 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.authorization; + +import org.apache.nifi.nar.NarCloseable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class UserGroupProviderInvocationHandler implements InvocationHandler { + + private final UserGroupProvider userGroupProvider; + private final ClassLoader classLoader; + + public UserGroupProviderInvocationHandler(final UserGroupProvider userGroupProvider, final ClassLoader classLoader) { + this.userGroupProvider = userGroupProvider; + this.classLoader = classLoader; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(classLoader)) { + return method.invoke(userGroupProvider, args); + } catch (final InvocationTargetException e) { + // If the proxied instance throws an Exception, it'll be wrapped in an InvocationTargetException. We want + // to instead re-throw what the proxied instance threw, so we pull it out of the InvocationTargetException. + throw e.getCause(); + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/AuthorizerFactoryTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/AuthorizerFactoryTest.java index 13d6f5a002..caae13f95e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/AuthorizerFactoryTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/AuthorizerFactoryTest.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.authorization; +import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.junit.Assert; import org.junit.Test; @@ -25,6 +26,9 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class AuthorizerFactoryTest { @Test(expected = AuthorizerCreationException.class) @@ -261,4 +265,83 @@ public class AuthorizerFactoryTest { } } + + @Test + public void testAuditInvoked() { + User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build(); + + AccessPolicy policy1 = new AccessPolicy.Builder() + .identifier("policy-id-1") + .resource("resource1") + .action(RequestAction.READ) + .addUser(user1.getIdentifier()) + .build(); + + Set policies = new LinkedHashSet<>(); + policies.add(policy1); + + Set users = new LinkedHashSet<>(); + users.add(user1); + + final MockPolicyBasedAuthorizer mockAuthorizer = new MockPolicyBasedAuthorizer(new HashSet<>(), users, policies); + + AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class); + Authorizer authorizer = AuthorizerFactory.installIntegrityChecks(mockAuthorizer); + authorizer.onConfigured(context); + + final AuthorizationRequest accessAttempt = new AuthorizationRequest.Builder() + .resource(new MockResource("resource1", "Resource 1")) + .identity("user-1") + .action(RequestAction.READ) + .accessAttempt(true) + .anonymous(false) + .build(); + + final AuthorizationResult accessAttemptResult = authorizer.authorize(accessAttempt); + + assertTrue(Result.Approved.equals(accessAttemptResult.getResult())); + assertTrue(mockAuthorizer.isAudited(accessAttempt)); + + final AuthorizationRequest nonAccessAttempt = new AuthorizationRequest.Builder() + .resource(new MockResource("resource1", "Resource 1")) + .identity("user-1") + .accessAttempt(false) + .action(RequestAction.READ) + .anonymous(false) + .build(); + + final AuthorizationResult nonAccessAttempResult = authorizer.authorize(nonAccessAttempt); + + assertTrue(Result.Approved.equals(nonAccessAttempResult.getResult())); + assertFalse(mockAuthorizer.isAudited(nonAccessAttempt)); + } + + /** + * Resource implementation for testing. + */ + private static class MockResource implements Resource { + + private final String identifier; + private final String name; + + public MockResource(String identifier, String name) { + this.identifier = identifier; + this.name = name; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getSafeDescription() { + return name; + } + } } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/MockPolicyBasedAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/MockPolicyBasedAuthorizer.java index 9b50b50ed4..b2a9662217 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/MockPolicyBasedAuthorizer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/test/java/org/apache/nifi/authorization/MockPolicyBasedAuthorizer.java @@ -27,12 +27,14 @@ import java.util.stream.Collectors; /** * Mock implementation of AbstractPolicyBasedAuthorizer. */ -public class MockPolicyBasedAuthorizer extends AbstractPolicyBasedAuthorizer { +public class MockPolicyBasedAuthorizer extends AbstractPolicyBasedAuthorizer implements AuthorizationAuditor { private Set groups = new HashSet<>(); private Set users = new HashSet<>(); private Set policies = new HashSet<>(); + private Set audited = new HashSet(); + public MockPolicyBasedAuthorizer() { } @@ -143,7 +145,9 @@ public class MockPolicyBasedAuthorizer extends AbstractPolicyBasedAuthorizer { return new UsersAndAccessPolicies() { @Override public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) { - return null; + return policies.stream() + .filter(policy -> policy.getResource().equals(resourceIdentifier) && policy.getAction().equals(action)) + .findFirst().orElse(null); } @Override @@ -165,6 +169,15 @@ public class MockPolicyBasedAuthorizer extends AbstractPolicyBasedAuthorizer { }; } + @Override + public void auditAccessAttempt(AuthorizationRequest request, AuthorizationResult result) { + audited.add(request); + } + + public boolean isAudited(AuthorizationRequest request) { + return audited.contains(request); + } + @Override public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java index 3b524bb606..95c5539321 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java @@ -62,6 +62,27 @@ public interface AuthorizableLookup { */ Authorizable getCounters(); + /** + * Get the authorizable for retrieving resources. + * + * @return authorizable + */ + Authorizable getResource(); + + /** + * Get the authorizable for site to site. + * + * @return authorizable + */ + Authorizable getSiteToSite(); + + /** + * Get the authorizable for the flow. + * + * @return authorizable + */ + Authorizable getFlow(); + /** * Get the authorizable RootGroup InputPort. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java index 5f8fb05e86..8cad74079a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java @@ -108,6 +108,42 @@ class StandardAuthorizableLookup implements AuthorizableLookup { } }; + private static final Authorizable RESOURCE_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getResourceResource(); + } + }; + + private static final Authorizable SITE_TO_SITE_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getSiteToSiteResource(); + } + }; + + private static final Authorizable FLOW_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getFlowResource(); + } + }; + private static final Authorizable SYSTEM_AUTHORIZABLE = new Authorizable() { @Override public Authorizable getParentAuthorizable() { @@ -268,6 +304,21 @@ class StandardAuthorizableLookup implements AuthorizableLookup { return COUNTERS_AUTHORIZABLE; } + @Override + public Authorizable getResource() { + return RESOURCE_AUTHORIZABLE; + } + + @Override + public Authorizable getSiteToSite() { + return SITE_TO_SITE_AUTHORIZABLE; + } + + @Override + public Authorizable getFlow() { + return FLOW_AUTHORIZABLE; + } + private ConfiguredComponent findControllerServiceReferencingComponent(final ControllerServiceReference referencingComponents, final String id) { ConfiguredComponent reference = null; for (final ConfiguredComponent component : referencingComponents.getReferencingComponents()) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java index 1444040282..9a8827a855 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java @@ -25,17 +25,11 @@ import org.apache.nifi.action.Operation; import org.apache.nifi.action.component.details.FlowChangeExtensionDetails; import org.apache.nifi.action.details.FlowChangeConfigureDetails; import org.apache.nifi.admin.service.AuditService; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.ComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; import org.apache.nifi.authorization.resource.Authorizable; -import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.ClusterCoordinator; @@ -100,29 +94,8 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration private void authorizeFlowAccess(final NiFiUser user) { // authorize access serviceFacade.authorizeAccess(lookup -> { - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getFlowResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to view the user interface.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + final Authorizable flow = lookup.getFlow(); + flow.authorize(authorizer, RequestAction.READ, user); }); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index a5acc6bff2..97cb4c954e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -24,17 +24,11 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.AuthorizeControllerServiceReference; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.ComponentAuthorizable; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.controller.FlowController; import org.apache.nifi.web.IllegalClusterResourceRequestException; @@ -71,8 +65,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.Date; -import java.util.HashMap; -import java.util.Map; /** * RESTful endpoint for managing a Flow Controller. @@ -97,42 +89,10 @@ public class ControllerResource extends ApplicationResource { * Authorizes access to the flow. */ private void authorizeController(final RequestAction action) { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getControllerResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(action) - .userContext(userContext) - .explanationSupplier(() -> { - final StringBuilder explanation = new StringBuilder("Unable to "); - - if (RequestAction.READ.equals(action)) { - explanation.append("view "); - } else { - explanation.append("modify "); - } - explanation.append("the controller."); - - return explanation.toString(); - }) - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable controller = lookup.getController(); + controller.authorize(authorizer, action, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java index c3f4a8df58..903b102463 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/CountersResource.java @@ -23,16 +23,9 @@ import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.manager.NodeResponse; import org.apache.nifi.cluster.manager.exception.UnknownNodeException; @@ -57,8 +50,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.HashMap; -import java.util.Map; /** @@ -81,42 +72,10 @@ public class CountersResource extends ApplicationResource { * Authorizes access to the flow. */ private void authorizeCounters(final RequestAction action) { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getCountersResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(action) - .userContext(userContext) - .explanationSupplier(() -> { - final StringBuilder explanation = new StringBuilder("Unable to "); - - if (RequestAction.READ.equals(action)) { - explanation.append("view "); - } else { - explanation.append("modify "); - } - explanation.append("counters."); - - return explanation.toString(); - }) - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable counters = lookup.getCounters(); + counters.authorize(authorizer, action, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index 94361a3687..889676c622 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -24,15 +24,9 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; import org.apache.nifi.authorization.resource.Authorizable; -import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.bundle.Bundle; @@ -204,31 +198,10 @@ public class FlowResource extends ApplicationResource { * Authorizes access to the flow. */ private void authorizeFlow() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getFlowResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to view the user interface.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable flow = lookup.getFlow(); + flow.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); } // ---- @@ -2225,29 +2198,8 @@ public class FlowResource extends ApplicationResource { } // a component for the specified id could not be found, attempt to authorize based on read to the controller - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getControllerResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> String.format("Unable to find component with id '%s' and unable to view the controller.", componentId)) - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + final Authorizable controller = lookup.getController(); + controller.authorize(authorizer, RequestAction.READ, user); }); // Note: History requests are not replicated throughout the cluster and are instead handled by the nodes independently diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java index c17ad8512e..55ed489d71 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java @@ -22,16 +22,9 @@ import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.api.dto.provenance.ProvenanceDTO; @@ -101,31 +94,10 @@ public class ProvenanceResource extends ApplicationResource { } private void authorizeProvenanceRequest() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getProvenanceResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to query provenance.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable provenance = lookup.getProvenance(); + provenance.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java index 1c91e5ed95..f957302471 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java @@ -21,16 +21,9 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.api.dto.ResourceDTO; @@ -43,9 +36,7 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * RESTful endpoint for retrieving system diagnostics. @@ -61,31 +52,10 @@ public class ResourceResource extends ApplicationResource { private Authorizer authorizer; private void authorizeResource() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getResourceResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to retrieve resources.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable resource = lookup.getResource(); + resource.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java index fc6d4dddac..2e17637027 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java @@ -22,16 +22,9 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.ClusterCoordinator; import org.apache.nifi.cluster.coordination.node.NodeWorkload; @@ -63,7 +56,6 @@ import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -99,31 +91,10 @@ public class SiteToSiteResource extends ApplicationResource { * Note: Protected for testing purposes */ protected void authorizeSiteToSite() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getSiteToSiteResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to retrieve site to site details.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable siteToSite = lookup.getSiteToSite(); + siteToSite.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java index 063f96cd7e..ff69e60848 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java @@ -22,16 +22,9 @@ import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.Authorization; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; -import org.apache.nifi.authorization.UserContextKeys; -import org.apache.nifi.authorization.resource.ResourceFactory; -import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.manager.NodeResponse; import org.apache.nifi.web.NiFiServiceFacade; @@ -47,8 +40,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.HashMap; -import java.util.Map; /** * RESTful endpoint for retrieving system diagnostics. @@ -64,31 +55,10 @@ public class SystemDiagnosticsResource extends ApplicationResource { private Authorizer authorizer; private void authorizeSystem() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - - final Map userContext; - if (!StringUtils.isBlank(user.getClientAddress())) { - userContext = new HashMap<>(); - userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress()); - } else { - userContext = null; - } - - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .resource(ResourceFactory.getSystemResource()) - .identity(user.getIdentity()) - .groups(user.getGroups()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(RequestAction.READ) - .userContext(userContext) - .explanationSupplier(() -> "Unable to view system diagnostics.") - .build(); - - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + serviceFacade.authorizeAccess(lookup -> { + final Authorizable system = lookup.getSystem(); + system.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); + }); } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java index 556c6bc453..ce8f18ac0e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/NiFiWebApiTest.java @@ -84,6 +84,7 @@ public class NiFiWebApiTest { processorEntity = response.getEntity(ProcessorEntity.class); processorDTO = processorEntity.getComponent(); String localSelectionId = processorDTO.getId(); + String localSelectionGroupId = processorDTO.getParentGroupId(); // ----------------------------------------------- // Create a termination processor @@ -114,6 +115,7 @@ public class NiFiWebApiTest { processorEntity = response.getEntity(ProcessorEntity.class); processorDTO = processorEntity.getComponent(); String terminationId = processorDTO.getId(); + String terminationGroupId = processorDTO.getParentGroupId(); // ----------------------------------------------- // Connect the two processors @@ -121,10 +123,12 @@ public class NiFiWebApiTest { ConnectableDTO source = new ConnectableDTO(); source.setId(localSelectionId); + source.setGroupId(localSelectionGroupId); source.setType(ConnectableType.PROCESSOR.name()); ConnectableDTO target = new ConnectableDTO(); target.setId(terminationId); + target.setGroupId(terminationGroupId); target.setType(ConnectableType.PROCESSOR.name()); // create the relationships diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java index dcc00d9101..bb2d4ebb1c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ITConnectionAccessControl.java @@ -358,11 +358,13 @@ public class ITConnectionAccessControl { // create the source connectable ConnectableDTO source = new ConnectableDTO(); source.setId(one.getId()); + source.setGroupId(one.getComponent().getParentGroupId()); source.setType(ConnectableType.PROCESSOR.name()); // create the target connectable ConnectableDTO target = new ConnectableDTO(); target.setId(two.getId()); + target.setGroupId(two.getComponent().getParentGroupId()); target.setType(ConnectableType.PROCESSOR.name()); // create the relationships diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java index 5636c2dc3c..ec162c7303 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java @@ -46,6 +46,7 @@ public abstract class NiFiAuthenticationProvider implements AuthenticationProvid public NiFiAuthenticationProvider(final NiFiProperties properties, final Authorizer authorizer) { this.properties = properties; this.mappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties)); + this.authorizer = authorizer; } public List getMappings() { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java index 510e1362d4..564171f44c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java @@ -18,12 +18,12 @@ package org.apache.nifi.web.security.x509; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authentication.AuthenticationResponse; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.UserContextKeys; +import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserDetails; @@ -50,6 +50,18 @@ import java.util.Set; */ public class X509AuthenticationProvider extends NiFiAuthenticationProvider { + private static final Authorizable PROXY_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getProxyResource(); + } + }; + private X509IdentityProvider certificateIdentityProvider; private Authorizer authorizer; @@ -94,27 +106,17 @@ public class X509AuthenticationProvider extends NiFiAuthenticationProvider { final Set groups = getUserGroups(identity); - if (chainIter.hasPrevious()) { - // authorize this proxy in order to authenticate this user - final AuthorizationRequest proxyAuthorizationRequest = new AuthorizationRequest.Builder() - .identity(identity) - .groups(groups) - .anonymous(isAnonymous) - .accessAttempt(true) - .action(RequestAction.WRITE) - .resource(ResourceFactory.getProxyResource()) - .userContext(proxy == null ? getUserContext(request) : null) // only set the context for the real user - .build(); + // Only set the client address for client making the request because we don't know the clientAddress of the proxied entities + String clientAddress = (proxy == null) ? request.getClientAddress() : null; + proxy = createUser(identity, groups, proxy, clientAddress, isAnonymous); - final AuthorizationResult proxyAuthorizationResult = authorizer.authorize(proxyAuthorizationRequest); - if (!Result.Approved.equals(proxyAuthorizationResult.getResult())) { + if (chainIter.hasPrevious()) { + try { + PROXY_AUTHORIZABLE.authorize(authorizer, RequestAction.WRITE, proxy); + } catch (final AccessDeniedException e) { throw new UntrustedProxyException(String.format("Untrusted proxy %s", identity)); } } - - // Only set the client address for user making the request because we don't know the client address of the proxies - String clientAddress = (proxy == null) ? request.getClientAddress() : null; - proxy = createUser(identity, groups, proxy, clientAddress, isAnonymous); } return new NiFiAuthenticationToken(new NiFiUserDetails(proxy)); diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java new file mode 100644 index 0000000000..7c16a8013c --- /dev/null +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java @@ -0,0 +1,205 @@ +/* + * 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.ranger.authorization; + +import org.apache.commons.lang.StringUtils; +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.AccessPolicyProvider; +import org.apache.nifi.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.authorization.ManagedAuthorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderLookup; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.Set; + +public class ManagedRangerAuthorizer extends RangerNiFiAuthorizer implements ManagedAuthorizer { + + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + + private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider"; + + private UserGroupProviderLookup userGroupProviderLookup; + private UserGroupProvider userGroupProvider; + private RangerBasePluginWithPolicies nifiPlugin; + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + userGroupProviderLookup = initializationContext.getUserGroupProviderLookup(); + + super.initialize(initializationContext); + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + final String userGroupProviderKey = configurationContext.getProperty("User Group Provider").getValue(); + userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderKey); + + // ensure the desired access policy provider has a user group provider + if (userGroupProvider == null) { + throw new AuthorizerCreationException(String.format("Unable to locate configured User Group Provider: %s", userGroupProviderKey)); + } + + super.onConfigured(configurationContext); + } + + @Override + protected RangerBasePluginWithPolicies createRangerBasePlugin(final String serviceType, final String appId) { + // override the method for creating the ranger base plugin so a user group provider can be specified + nifiPlugin = new RangerBasePluginWithPolicies(serviceType, appId, userGroupProvider); + return nifiPlugin; + } + + @Override + public AccessPolicyProvider getAccessPolicyProvider() { + return new AccessPolicyProvider() { + @Override + public Set getAccessPolicies() throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicies(); + } + + @Override + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicy(identifier); + } + + @Override + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicy(resourceIdentifier, action); + } + + @Override + public UserGroupProvider getUserGroupProvider() { + return userGroupProvider; + } + + @Override + public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } + }; + } + + @Override + public String getFingerprint() throws AuthorizationAccessException { + final StringWriter out = new StringWriter(); + try { + // create the document + final DocumentBuilder documentBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + final Document document = documentBuilder.newDocument(); + + // create the root element + final Element managedRangerAuthorizationsElement = document.createElement("managedRangerAuthorizations"); + document.appendChild(managedRangerAuthorizationsElement); + + // create the user group provider element + final Element userGroupProviderElement = document.createElement(USER_GROUP_PROVIDER_ELEMENT); + managedRangerAuthorizationsElement.appendChild(userGroupProviderElement); + + // append fingerprint if the provider is configurable + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + userGroupProviderElement.appendChild(document.createTextNode(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint())); + } + + final Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.transform(new DOMSource(document), new StreamResult(out)); + } catch (ParserConfigurationException | TransformerException e) { + throw new AuthorizationAccessException("Unable to generate fingerprint", e); + } + + return out.toString(); + } + + @Override + public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { + if (StringUtils.isBlank(fingerprint)) { + return; + } + + final String userGroupFingerprint = parseFingerprint(fingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint) && userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(userGroupFingerprint); + } + } + + @Override + public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { + final String userGroupFingerprint = parseFingerprint(proposedFingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint)) { + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(userGroupFingerprint); + } else { + throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting."); + } + } + } + + private final String parseFingerprint(final String fingerprint) throws AuthorizationAccessException { + final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8); + + try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) { + final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + final Document document = docBuilder.parse(in); + final Element rootElement = document.getDocumentElement(); + + final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT); + if (userGroupProviderList.getLength() != 1) { + throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint)); + } + + final Node userGroupProvider = userGroupProviderList.item(0); + return userGroupProvider.getTextContent(); + } catch (SAXException | ParserConfigurationException | IOException e) { + throw new AuthorizationAccessException("Unable to parse fingerprint", e); + } + } +} diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java index 8b664de618..c74a7d1b9d 100644 --- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java @@ -18,38 +18,55 @@ */ package org.apache.nifi.ranger.authorization; +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.User; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.util.StringUtils; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.ServicePolicies; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import java.util.stream.Collectors; /** - * Extends the base plugin to add ability to check if a policy exists for a given resource. + * Extends the base plugin to convert service policies into NiFi policy domain model. */ public class RangerBasePluginWithPolicies extends RangerBasePlugin { - private AtomicReference> resources = new AtomicReference<>(new HashSet<>()); + private static final Logger logger = LoggerFactory.getLogger(RangerBasePluginWithPolicies.class); - public RangerBasePluginWithPolicies(String serviceType, String appId) { + private UserGroupProvider userGroupProvider; + private AtomicReference policies = new AtomicReference<>(new PolicyLookup()); + + public RangerBasePluginWithPolicies(final String serviceType, final String appId) { + this(serviceType, appId, null); + } + + public RangerBasePluginWithPolicies(final String serviceType, final String appId, final UserGroupProvider userGroupProvider) { super(serviceType, appId); + this.userGroupProvider = userGroupProvider; // will be null if used outside of the ManagedRangerAuthorizer } @Override - public void setPolicies(ServicePolicies policies) { + public void setPolicies(final ServicePolicies policies) { super.setPolicies(policies); if (policies == null || policies.getPolicies() == null) { - this.resources.set(new HashSet<>()); + this.policies.set(new PolicyLookup()); } else { - final Set newResources = policies.getPolicies().stream() - .flatMap(p -> p.getResources().values().stream()) - .flatMap(r -> r.getValues().stream()) - .collect(Collectors.toSet()); - - this.resources.set(newResources); + this.policies.set(createPolicyLookup(policies)); } } @@ -60,16 +77,197 @@ public class RangerBasePluginWithPolicies extends RangerBasePlugin { * * @return true if a policy exists for the given resource, false otherwise */ - public boolean doesPolicyExist(String resourceIdentifier) { + public boolean doesPolicyExist(final String resourceIdentifier, final RequestAction requestAction) { if (resourceIdentifier == null) { return false; } - final Set currResources = resources.get(); - if (currResources == null) { - return false; + final PolicyLookup policyLookup = policies.get(); + return policyLookup.getAccessPolicy(resourceIdentifier, requestAction) != null; + } + + public Set getAccessPolicies() throws AuthorizationAccessException { + return policies.get().getAccessPolicies(); + } + + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(identifier); + } + + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(resourceIdentifier, action); + } + + private PolicyLookup createPolicyLookup(final ServicePolicies servicePolicies) { + final Map policiesByIdentifier = new HashMap<>(); + final Map> policiesByResource = new HashMap<>(); + + logger.info("Converting Ranger ServicePolicies model into NiFi policy model for viewing purposes in NiFi UI."); + + servicePolicies.getPolicies().stream().forEach(policy -> { + // only consider policies that are enabled + if (Boolean.TRUE.equals(policy.getIsEnabled())) { + // get all the resources for this policy - excludes/recursive support disabled + final Set resources = policy.getResources().values().stream() + .filter(resource -> { + final boolean isExclude = Boolean.TRUE.equals(resource.getIsExcludes()); + final boolean isRecursive = Boolean.TRUE.equals(resource.getIsRecursive()); + + if (isExclude) { + logger.warn(String.format("Resources [%s] marked as an exclude policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + if (isRecursive) { + logger.warn(String.format("Resources [%s] marked as a recursive policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + + return !isExclude && !isRecursive; + }) + .flatMap(resource -> resource.getValues().stream()) + .collect(Collectors.toSet()); + + policy.getPolicyItems().forEach(policyItem -> { + // get all the users for this policy item, excluding unknown users + final Set userIds = policyItem.getUsers().stream() + .map(userIdentity -> getUser(userIdentity)) + .filter(Objects::nonNull) + .map(user -> user.getIdentifier()) + .collect(Collectors.toSet()); + + // get all groups for this policy item, excluding unknown groups + final Set groupIds = policyItem.getGroups().stream() + .map(groupName -> getGroup(groupName)) + .filter(Objects::nonNull) + .map(group -> group.getIdentifier()) + .collect(Collectors.toSet()); + + // check if this policy item is a delegate admin + final boolean isDelegateAdmin = Boolean.TRUE.equals(policyItem.getDelegateAdmin()); + + policyItem.getAccesses().forEach(access -> { + try { + // interpret the request action + final RequestAction action = RequestAction.valueOf(access.getType()); + + // function for creating an access policy + final Function createPolicy = resource -> new AccessPolicy.Builder() + .identifierGenerateFromSeed(resource + access.getType()) + .resource(resource) + .action(action) + .addUsers(userIds) + .addGroups(groupIds) + .build(); + + resources.forEach(resource -> { + // create the access policy for the specified resource + final AccessPolicy accessPolicy = createPolicy.apply(resource); + policiesByIdentifier.put(accessPolicy.getIdentifier(), accessPolicy); + policiesByResource.computeIfAbsent(resource, r -> new HashMap<>()).put(action, accessPolicy); + + // if this is a delegate admin, also create the admin policy for the specified resource + if (isDelegateAdmin) { + // build the admin resource identifier + final String adminResource; + if (resource.startsWith("/")) { + adminResource = "/policies" + resource; + } else { + adminResource = "/policies/" + resource; + } + + final AccessPolicy adminAccessPolicy = createPolicy.apply(adminResource); + policiesByIdentifier.put(adminAccessPolicy.getIdentifier(), adminAccessPolicy); + policiesByResource.computeIfAbsent(adminResource, ar -> new HashMap<>()).put(action, adminAccessPolicy); + } + }); + } catch (final IllegalArgumentException e) { + logger.warn(String.format("Unrecognized request action '%s'. Skipping policy for viewing purposes. Will still be used for access decisions.", access.getType())); + } + }); + }); + } + }); + + return new PolicyLookup(policiesByIdentifier, policiesByResource); + } + + private User getUser(final String identity) { + if (userGroupProvider == null) { + // generate the user deterministically when running outside of the ManagedRangerAuthorizer + return new User.Builder().identifierGenerateFromSeed(identity).identity(identity).build(); } else { - return currResources.contains(resourceIdentifier); + // find the user in question + final User user = userGroupProvider.getUserByIdentity(identity); + + if (user == null) { + logger.warn(String.format("Cannot find user '%s' in the configured User Group Provider. Skipping user for viewing purposes. Will still be used for access decisions.", identity)); + } + + return user; + } + } + + private Group getGroup(final String name) { + if (userGroupProvider == null) { + // generate the group deterministically when running outside of the ManagedRangerAuthorizer + return new Group.Builder().identifierGenerateFromSeed(name).name(name).build(); + } else { + // find the group in question + final Group group = userGroupProvider.getGroups().stream().filter(g -> g.getName().equals(name)).findFirst().orElse(null); + + if (group == null) { + logger.warn(String.format("Cannot find group '%s' in the configured User Group Provider. Skipping group for viewing purposes. Will still be used for access decisions.", name)); + } + + return group; + } + } + + private static class PolicyLookup { + + private final Map policiesByIdentifier; + private final Map> policiesByResource; + private final Set allPolicies; + + private PolicyLookup() { + this(null, null); + } + + private PolicyLookup(final Map policiesByIdentifier, final Map> policiesByResource) { + if (policiesByIdentifier == null) { + allPolicies = Collections.EMPTY_SET; + } else { + allPolicies = Collections.unmodifiableSet(new HashSet<>(policiesByIdentifier.values())); + } + + this.policiesByIdentifier = policiesByIdentifier; + this.policiesByResource = policiesByResource; + } + + private Set getAccessPolicies() throws AuthorizationAccessException { + return allPolicies; + } + + private AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + if (policiesByIdentifier == null) { + return null; + } + + return policiesByIdentifier.get(identifier); + } + + private AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + if (policiesByResource == null) { + return null; + } + + final Map policiesForResource = policiesByResource.get(resourceIdentifier); + + if (policiesForResource != null) { + return policiesForResource.get(action); + } + + return null; } } diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java index 41b81062d3..8b98d53d89 100644 --- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java @@ -21,6 +21,7 @@ package org.apache.nifi.ranger.authorization; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.authorization.AuthorizationAuditor; import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.Authorizer; @@ -33,23 +34,26 @@ import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.apache.nifi.authorization.exception.AuthorizerDestructionException; import org.apache.nifi.components.PropertyValue; import org.apache.nifi.util.NiFiProperties; +import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler; import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; import org.apache.ranger.plugin.policyengine.RangerAccessResult; -import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.net.MalformedURLException; import java.util.Date; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * Authorizer implementation that uses Apache Ranger to make authorization decisions. */ -public class RangerNiFiAuthorizer implements Authorizer { +public class RangerNiFiAuthorizer implements Authorizer, AuthorizationAuditor { private static final Logger logger = LoggerFactory.getLogger(RangerNiFiAuthorizer.class); @@ -67,6 +71,8 @@ public class RangerNiFiAuthorizer implements Authorizer { static final String HADOOP_SECURITY_AUTHENTICATION = "hadoop.security.authentication"; static final String KERBEROS_AUTHENTICATION = "kerberos"; + private final ConcurrentMap resultLookup = new ConcurrentHashMap<>(); + private volatile RangerBasePluginWithPolicies nifiPlugin = null; private volatile RangerDefaultAuditHandler defaultAuditHandler = null; private volatile String rangerAdminIdentity = null; @@ -135,6 +141,7 @@ public class RangerNiFiAuthorizer implements Authorizer { @Override public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { final String identity = request.getIdentity(); + final Set userGroups = request.getGroups(); final String resourceIdentifier = request.getResource().getIdentifier(); // if a ranger admin identity was provided, and it equals the identity making the request, @@ -159,24 +166,25 @@ public class RangerNiFiAuthorizer implements Authorizer { rangerRequest.setAction(request.getAction().name()); rangerRequest.setAccessType(request.getAction().name()); rangerRequest.setUser(identity); + rangerRequest.setUserGroups(userGroups); rangerRequest.setAccessTime(new Date()); if (!StringUtils.isBlank(clientIp)) { rangerRequest.setClientIPAddress(clientIp); } - // for a direct access request use the default audit handler so we generate audit logs - // for non-direct access provide a null result processor so no audit logs get generated - final RangerAccessResultProcessor resultProcessor = request.isAccessAttempt() ? defaultAuditHandler : null; - - final RangerAccessResult result = nifiPlugin.isAccessAllowed(rangerRequest, resultProcessor); + final RangerAccessResult result = nifiPlugin.isAccessAllowed(rangerRequest); if (result != null && result.getIsAllowed()) { + // store the result for auditing purposes later + resultLookup.put(request, result); + + // return approved return AuthorizationResult.approved(); } else { // if result.getIsAllowed() is false, then we need to determine if it was because no policy exists for the // given resource, or if it was because a policy exists but not for the given user or action - final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier()); + final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier(), request.getAction()); if (doesPolicyExist) { final String reason = result == null ? null : result.getReason(); @@ -184,6 +192,9 @@ public class RangerNiFiAuthorizer implements Authorizer { logger.debug(String.format("Unable to authorize %s due to %s", identity, reason)); } + // store the result for auditing purposes later + resultLookup.put(request, result); + // a policy does exist for the resource so we were really denied access here return AuthorizationResult.denied(request.getExplanationSupplier().get()); } else { @@ -193,6 +204,21 @@ public class RangerNiFiAuthorizer implements Authorizer { } } + @Override + public void auditAccessAttempt(final AuthorizationRequest request, final AuthorizationResult result) { + final RangerAccessResult rangerResult = resultLookup.remove(request); + + if (rangerResult != null && rangerResult.getIsAudited()) { + AuthzAuditEvent event = defaultAuditHandler.getAuthzEvents(rangerResult); + + // update the event with the originally requested resource + event.setResourceType(RANGER_NIFI_RESOURCE_NAME); + event.setResourcePath(request.getRequestedResource().getIdentifier()); + + defaultAuditHandler.logAuthzAudit(event); + } + } + @Override public void preDestruction() throws AuthorizerDestructionException { if (nifiPlugin != null) { diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer index 607d979e0e..34d87976e8 100755 --- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer @@ -13,3 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. org.apache.nifi.ranger.authorization.RangerNiFiAuthorizer +org.apache.nifi.ranger.authorization.ManagedRangerAuthorizer diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java new file mode 100644 index 0000000000..bd1f6e1fbc --- /dev/null +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java @@ -0,0 +1,200 @@ +/* + * 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.ranger.authorization; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderLookup; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.util.MockPropertyValue; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +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 ManagedRangerAuthorizerTest { + + private static final String TENANT_FINGERPRINT = + "" + + "" + + "" + + "" + + "" + + ""; + + private static final String EMPTY_FINGERPRINT = "" + + "" + + "" + + ""; + + private static final String NON_EMPTY_FINGERPRINT = "" + + "" + + "" + + "<tenants>" + + "<user identifier=\"user-id-1\" identity=\"user-1\"></user>" + + "<group identifier=\"group-id-1\" name=\"group-1\">" + + "<groupUser identifier=\"user-id-1\"></groupUser>" + + "</group>" + + "</tenants>" + + "" + + ""; + + private final String serviceType = "nifiService"; + private final String appId = "nifiAppId"; + + @Before + public void setup() { + // have to initialize this system property before anything else + File krb5conf = new File("src/test/resources/krb5.conf"); + assertTrue(krb5conf.exists()); + System.setProperty("java.security.krb5.conf", krb5conf.getAbsolutePath()); + + // rest the authentication to simple in case any tests set it to kerberos + final Configuration securityConf = new Configuration(); + securityConf.set(RangerNiFiAuthorizer.HADOOP_SECURITY_AUTHENTICATION, "simple"); + UserGroupInformation.setConfiguration(securityConf); + + assertFalse(UserGroupInformation.isSecurityEnabled()); + } + + @Test + public void testNonConfigurableFingerPrint() throws Exception { + final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + Assert.assertEquals(EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testConfigurableEmptyFingerPrint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(""); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + Assert.assertEquals(EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testConfigurableFingerPrint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(TENANT_FINGERPRINT); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + Assert.assertEquals(NON_EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testInheritEmptyFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.inheritFingerprint(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test(expected = AuthorizationAccessException.class) + public void testInheritInvalidFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.inheritFingerprint("not a valid fingerprint"); + } + + @Test + public void testInheritNonEmptyFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.inheritFingerprint(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).inheritFingerprint(TENANT_FINGERPRINT); + } + + @Test + public void testCheckInheritEmptyFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test(expected = AuthorizationAccessException.class) + public void testCheckInheritInvalidFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability("not a valid fingerprint"); + } + + @Test + public void testCheckInheritNonEmptyFingerprint() throws Exception { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).checkInheritability(TENANT_FINGERPRINT); + } + + @Test(expected = UninheritableAuthorizationsException.class) + public void testCheckInheritNonConfigurableUserGroupProvider() throws Exception { + final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability(NON_EMPTY_FINGERPRINT); + } + + private ManagedRangerAuthorizer getStandardManagedAuthorizer(final UserGroupProvider userGroupProvider) { + final ManagedRangerAuthorizer managedAuthorizer = new ManagedRangerAuthorizer(); + + final AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class); + when(configurationContext.getProperty(eq("User Group Provider"))).thenReturn(new MockPropertyValue("user-group-provider", null)); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_SECURITY_PATH_PROP))).thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-security.xml")); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_AUDIT_PATH_PROP))).thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-audit.xml")); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_APP_ID_PROP))).thenReturn(new MockPropertyValue(appId)); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_SERVICE_TYPE_PROP))).thenReturn(new MockPropertyValue(serviceType)); + + final UserGroupProviderLookup userGroupProviderLookup = mock(UserGroupProviderLookup.class); + when(userGroupProviderLookup.getUserGroupProvider("user-group-provider")).thenReturn(userGroupProvider); + + final AuthorizerInitializationContext initializationContext = mock(AuthorizerInitializationContext.class); + when(initializationContext.getUserGroupProviderLookup()).thenReturn(userGroupProviderLookup); + + managedAuthorizer.initialize(initializationContext); + managedAuthorizer.onConfigured(configurationContext); + + return managedAuthorizer; + } +} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java index 6a12ba7497..a9f38ba369 100644 --- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java @@ -18,52 +18,463 @@ */ package org.apache.nifi.ranger.authorization; +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.User; +import org.apache.nifi.authorization.UserAndGroups; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.util.ServicePolicies; -import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class TestRangerBasePluginWithPolicies { @Test - public void testDoesPolicyExist() { - final String resourceIdentifier1 = "resource1"; - RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + public void testPoliciesWithoutUserGroupProvider() { + final String user1 = "user-1"; + final String group1 = "group-1"; - final Map policy1Resources = new HashMap<>(); + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); policy1Resources.put(resourceIdentifier1, resource1); + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + final RangerPolicy policy1 = new RangerPolicy(); policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); - final String resourceIdentifier2 = "resource2"; - RangerPolicy.RangerPolicyResource resource2 = new RangerPolicy.RangerPolicyResource(resourceIdentifier2); + final String resourceIdentifier2 = "/resource-2"; + RangerPolicyResource resource2 = new RangerPolicyResource(resourceIdentifier2); - final Map policy2Resources = new HashMap<>(); + final Map policy2Resources = new HashMap<>(); policy2Resources.put(resourceIdentifier2, resource2); + final RangerPolicyItem policy2Item = new RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + final RangerPolicy policy2 = new RangerPolicy(); policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); final List policies = new ArrayList<>(); policies.add(policy1); policies.add(policy2); + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + final ServicePolicies servicePolicies = new ServicePolicies(); servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); // set all the policies in the plugin final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); pluginWithPolicies.setPolicies(servicePolicies); - Assert.assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1)); - Assert.assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2)); - Assert.assertFalse(pluginWithPolicies.doesPolicyExist("resource3")); + // ensure the two ranger policies converted into 3 nifi access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertTrue(readResource1.equals(pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier()))); + assertEquals(1, readResource1.getUsers().size()); + assertTrue(readResource1.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user1).identity(user1).build().getIdentifier())); + assertTrue(readResource1.getGroups().isEmpty()); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertTrue(readResource2.equals(pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier()))); + assertTrue(readResource2.getUsers().isEmpty()); + assertEquals(1, readResource2.getGroups().size()); + assertTrue(readResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertTrue(writeResource2.equals(pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier()))); + assertTrue(writeResource2.getUsers().isEmpty()); + assertEquals(1, writeResource2.getGroups().size()); + assertTrue(writeResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // resource 3 -> no read or write + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.WRITE)); + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.READ)); + + // no read or write + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.WRITE)); + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.READ)); } + @Test + public void testNoPolicies() { + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + + assertFalse(pluginWithPolicies.doesPolicyExist("non-existent-resource", RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-identifier")); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-resource", RequestAction.READ)); + } + + @Test + public void testDisabledPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setIsEnabled(false); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + } + + @Test + public void testExcludesPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + resource1.setIsExcludes(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testRecursivePolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + resource1.setIsRecursive(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testDelegateAdmin() { + final String user1 = "user-1"; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setDelegateAdmin(true); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + assertEquals(4, pluginWithPolicies.getAccessPolicies().size()); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testPoliciesWithUserGroupProvider() { + final String user1 = "user-1"; // unknown according to user group provider + final String user2 = "user-2"; // known according to user group provider + final String group1 = "group-1"; // unknown according to user group provider + final String group2 = "group-2"; // known according to user group provider + + final UserGroupProvider userGroupProvider = new UserGroupProvider() { + @Override + public Set getUsers() throws AuthorizationAccessException { + return Stream.of(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build()).collect(Collectors.toSet()); + } + + @Override + public User getUser(String identifier) throws AuthorizationAccessException { + final User u2 = new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + if (u2.getIdentifier().equals(identifier)) { + return u2; + } else { + return null; + } + } + + @Override + public User getUserByIdentity(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } else { + return null; + } + } + + @Override + public Set getGroups() throws AuthorizationAccessException { + return Stream.of(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build()).collect(Collectors.toSet()); + } + + @Override + public Group getGroup(String identifier) throws AuthorizationAccessException { + final Group g2 = new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build(); + if (g2.getIdentifier().equals(identifier)) { + return g2; + } else { + return null; + } + } + + @Override + public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new UserAndGroups() { + @Override + public User getUser() { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } + + @Override + public Set getGroups() { + return Collections.EMPTY_SET; + } + }; + } else { + return null; + } + } + + @Override + public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } + }; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setGroups(Stream.of(group2).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final String resourceIdentifier2 = "/resource-2"; + RangerPolicyResource resource2 = new RangerPolicyResource(resourceIdentifier2); + + final Map policy2Resources = new HashMap<>(); + policy2Resources.put(resourceIdentifier2, resource2); + + final RangerPolicyItem policy2Item = new RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setUsers(Stream.of(user2).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + + final RangerPolicy policy2 = new RangerPolicy(); + policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + policies.add(policy2); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi", userGroupProvider); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the two ranger policies converted into 3 nifi access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertTrue(readResource1.equals(pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier()))); + assertTrue(readResource1.getUsers().isEmpty()); + assertEquals(1, readResource1.getGroups().size()); + assertTrue(readResource1.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build().getIdentifier())); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertTrue(readResource2.equals(pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier()))); + assertEquals(1, readResource2.getUsers().size()); + assertTrue(readResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(readResource2.getGroups().isEmpty()); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertTrue(writeResource2.equals(pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier()))); + assertEquals(1, writeResource2.getUsers().size()); + assertTrue(writeResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(writeResource2.getGroups().isEmpty()); + } } diff --git a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java index 679d2cbd03..a7a1b1029b 100644 --- a/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java +++ b/nifi-nar-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java @@ -261,8 +261,7 @@ public class TestRangerNiFiAuthorizer { // a non-null result processor should be used for direct access when(rangerBasePlugin.isAccessAllowed( - argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), - notNull(RangerAccessResultProcessor.class)) + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) ).thenReturn(allowedResult); final AuthorizationResult result = authorizer.authorize(request); @@ -297,8 +296,7 @@ public class TestRangerNiFiAuthorizer { // no result processor should be provided used non-direct access when(rangerBasePlugin.isAccessAllowed( - argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), - eq(null)) + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) ).thenReturn(allowedResult); final AuthorizationResult result = authorizer.authorize(request); @@ -338,7 +336,7 @@ public class TestRangerNiFiAuthorizer { ).thenReturn(notAllowedResult); // return false when checking if a policy exists for the resource - when(rangerBasePlugin.doesPolicyExist(systemResource)).thenReturn(false); + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(false); final AuthorizationResult result = authorizer.authorize(request); assertEquals(AuthorizationResult.resourceNotFound().getResult(), result.getResult()); @@ -372,12 +370,11 @@ public class TestRangerNiFiAuthorizer { // no result processor should be provided used non-direct access when(rangerBasePlugin.isAccessAllowed( - argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), - notNull(RangerAccessResultProcessor.class)) + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) ).thenReturn(notAllowedResult); // return true when checking if a policy exists for the resource - when(rangerBasePlugin.doesPolicyExist(systemResource)).thenReturn(true); + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(true); final AuthorizationResult result = authorizer.authorize(request); assertEquals(AuthorizationResult.denied().getResult(), result.getResult()); @@ -427,12 +424,11 @@ public class TestRangerNiFiAuthorizer { expectedRangerRequest.setUser(request.getIdentity()); // return true when checking if a policy exists for the resource - when(rangerBasePlugin.doesPolicyExist(resourceIdentifier)).thenReturn(true); + when(rangerBasePlugin.doesPolicyExist(resourceIdentifier, action)).thenReturn(true); // a non-null result processor should be used for direct access when(rangerBasePlugin.isAccessAllowed( - argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), - notNull(RangerAccessResultProcessor.class)) + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) ).thenReturn(notAllowedResult); final AuthorizationResult result = authorizer.authorize(request); diff --git a/pom.xml b/pom.xml index 54f8e9c494..7c741255ec 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ 12.0.1 4.2.5 2.2.0 - 0.6.0 + 0.7.1 1.2.1 2.6.2 1.1.2