NIFI-2695: - Providing more granular and meaningful authorization error messages.

This closes #1309.

Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
Matt Gilman 2016-12-08 11:29:32 -05:00 committed by Bryan Bende
parent 41189b055f
commit b1c9f0e764
No known key found for this signature in database
GPG Key ID: A0DDA9ED50711C39
29 changed files with 400 additions and 100 deletions

View File

@ -158,8 +158,7 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
return AuthorizationResult.approved(); return AuthorizationResult.approved();
} }
return AuthorizationResult.denied(request.getExplanationSupplier().get());
return AuthorizationResult.denied();
} }
/** /**

View File

@ -20,12 +20,15 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier;
/** /**
* Represents an authorization request for a given user/entity performing an action against a resource within some userContext. * Represents an authorization request for a given user/entity performing an action against a resource within some userContext.
*/ */
public class AuthorizationRequest { public class AuthorizationRequest {
public static final String DEFAULT_EXPLANATION = "Unable to perform the desired action.";
private final Resource resource; private final Resource resource;
private final String identity; private final String identity;
private final RequestAction action; private final RequestAction action;
@ -33,6 +36,7 @@ public class AuthorizationRequest {
private final boolean isAnonymous; private final boolean isAnonymous;
private final Map<String, String> userContext; private final Map<String, String> userContext;
private final Map<String, String> resourceContext; private final Map<String, String> resourceContext;
private final Supplier<String> explanationSupplier;
private AuthorizationRequest(final Builder builder) { private AuthorizationRequest(final Builder builder) {
Objects.requireNonNull(builder.resource, "The resource is required when creating an authorization request"); Objects.requireNonNull(builder.resource, "The resource is required when creating an authorization request");
@ -47,6 +51,16 @@ public class AuthorizationRequest {
this.isAnonymous = builder.isAnonymous; this.isAnonymous = builder.isAnonymous;
this.userContext = builder.userContext == null ? null : Collections.unmodifiableMap(builder.userContext); this.userContext = builder.userContext == null ? null : Collections.unmodifiableMap(builder.userContext);
this.resourceContext = builder.resourceContext == null ? null : Collections.unmodifiableMap(builder.resourceContext); this.resourceContext = builder.resourceContext == null ? null : Collections.unmodifiableMap(builder.resourceContext);
this.explanationSupplier = () -> {
final String explanation = builder.explanationSupplier.get();
// ensure the specified supplier returns non null
if (explanation == null) {
return DEFAULT_EXPLANATION;
} else {
return explanation;
}
};
} }
/** /**
@ -112,6 +126,15 @@ public class AuthorizationRequest {
return resourceContext; return resourceContext;
} }
/**
* A supplier for the explanation if access is denied. Non null.
*
* @return The explanation supplier if access is denied
*/
public Supplier<String> getExplanationSupplier() {
return explanationSupplier;
}
/** /**
* AuthorizationRequest builder. * AuthorizationRequest builder.
*/ */
@ -124,6 +147,7 @@ public class AuthorizationRequest {
private RequestAction action; private RequestAction action;
private Map<String, String> userContext; private Map<String, String> userContext;
private Map<String, String> resourceContext; private Map<String, String> resourceContext;
private Supplier<String> explanationSupplier = () -> DEFAULT_EXPLANATION;
public Builder resource(final Resource resource) { public Builder resource(final Resource resource) {
this.resource = resource; this.resource = resource;
@ -164,6 +188,13 @@ public class AuthorizationRequest {
return this; return this;
} }
public Builder explanationSupplier(final Supplier<String> explanationSupplier) {
if (explanationSupplier != null) {
this.explanationSupplier = explanationSupplier;
}
return this;
}
public AuthorizationRequest build() { public AuthorizationRequest build() {
return new AuthorizationRequest(this); return new AuthorizationRequest(this);
} }

View File

@ -28,7 +28,7 @@ public class AuthorizationResult {
} }
private static final AuthorizationResult APPROVED = new AuthorizationResult(Result.Approved, null); private static final AuthorizationResult APPROVED = new AuthorizationResult(Result.Approved, null);
private static final AuthorizationResult RESOURCE_NOT_FOUND = new AuthorizationResult(Result.ResourceNotFound, null); private static final AuthorizationResult RESOURCE_NOT_FOUND = new AuthorizationResult(Result.ResourceNotFound, "Not authorized for the requested resource.");
private final Result result; private final Result result;
private final String explanation; private final String explanation;
@ -44,6 +44,10 @@ public class AuthorizationResult {
throw new IllegalArgumentException("An explanation is required when the authorization request is denied."); throw new IllegalArgumentException("An explanation is required when the authorization request is denied.");
} }
if (Result.ResourceNotFound.equals(result) && explanation == null) {
throw new IllegalArgumentException("An explanation is required when the authorization request is resource not found.");
}
this.result = result; this.result = result;
this.explanation = explanation; this.explanation = explanation;
} }
@ -83,7 +87,7 @@ public class AuthorizationResult {
* @return a new denied AuthorizationResult * @return a new denied AuthorizationResult
*/ */
public static AuthorizationResult denied() { public static AuthorizationResult denied() {
return denied("Access is denied"); return denied(AuthorizationRequest.DEFAULT_EXPLANATION);
} }
/** /**

View File

@ -34,4 +34,11 @@ public interface Resource {
* @return name of this resource * @return name of this resource
*/ */
String getName(); String getName();
/**
* The description of this resource that may be safely used in messages to the client.
*
* @return safe description
*/
String getSafeDescription();
} }

View File

@ -26,8 +26,8 @@ import org.apache.nifi.authorization.Resource;
import org.apache.nifi.authorization.UserContextKeys; import org.apache.nifi.authorization.UserContextKeys;
import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUser;
import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
public interface Authorizable { public interface Authorizable {
@ -70,7 +70,7 @@ public interface Authorizable {
*/ */
default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) { default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
if (user == null) { if (user == null) {
return AuthorizationResult.denied("Unknown user"); return AuthorizationResult.denied("Unknown user.");
} }
final Map<String,String> userContext; final Map<String,String> userContext;
@ -81,15 +81,28 @@ public interface Authorizable {
userContext = null; userContext = null;
} }
// build the request final Resource resource = getResource();
final AuthorizationRequest request = new AuthorizationRequest.Builder() final AuthorizationRequest request = new AuthorizationRequest.Builder()
.identity(user.getIdentity()) .identity(user.getIdentity())
.anonymous(user.isAnonymous()) .anonymous(user.isAnonymous())
.accessAttempt(false) .accessAttempt(false)
.action(action) .action(action)
.resource(getResource()) .resource(resource)
.resourceContext(resourceContext) .resourceContext(resourceContext)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> {
// build the safe explanation
final StringBuilder safeDescription = new StringBuilder("Unable to ");
if (RequestAction.READ.equals(action)) {
safeDescription.append("view ");
} else {
safeDescription.append("modify ");
}
safeDescription.append(resource.getSafeDescription()).append(".");
return safeDescription.toString();
})
.build(); .build();
// perform the authorization // perform the authorization
@ -99,9 +112,37 @@ public interface Authorizable {
if (Result.ResourceNotFound.equals(result.getResult())) { if (Result.ResourceNotFound.equals(result.getResult())) {
final Authorizable parent = getParentAuthorizable(); final Authorizable parent = getParentAuthorizable();
if (parent == null) { if (parent == null) {
return AuthorizationResult.denied(); return AuthorizationResult.denied("No applicable policies could be found.");
} else { } else {
return parent.checkAuthorization(authorizer, action, user, resourceContext); // create a custom authorizable to override the safe description but still defer to the parent authorizable
final Authorizable parentProxy = new Authorizable() {
@Override
public Authorizable getParentAuthorizable() {
return parent.getParentAuthorizable();
}
@Override
public Resource getResource() {
final Resource parentResource = parent.getResource();
return new Resource() {
@Override
public String getIdentifier() {
return parentResource.getIdentifier();
}
@Override
public String getName() {
return parentResource.getName();
}
@Override
public String getSafeDescription() {
return resource.getSafeDescription();
}
};
}
};
return parentProxy.checkAuthorization(authorizer, action, user, resourceContext);
} }
} else { } else {
return result; return result;
@ -133,7 +174,7 @@ public interface Authorizable {
*/ */
default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException { default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unknown user"); throw new AccessDeniedException("Unknown user.");
} }
final Map<String,String> userContext; final Map<String,String> userContext;
@ -144,23 +185,65 @@ public interface Authorizable {
userContext = null; userContext = null;
} }
final Resource resource = getResource();
final AuthorizationRequest request = new AuthorizationRequest.Builder() final AuthorizationRequest request = new AuthorizationRequest.Builder()
.identity(user.getIdentity()) .identity(user.getIdentity())
.anonymous(user.isAnonymous()) .anonymous(user.isAnonymous())
.accessAttempt(true) .accessAttempt(true)
.action(action) .action(action)
.resource(getResource()) .resource(resource)
.resourceContext(resourceContext) .resourceContext(resourceContext)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> {
// build the safe explanation
final StringBuilder safeDescription = new StringBuilder("Unable to ");
if (RequestAction.READ.equals(action)) {
safeDescription.append("view ");
} else {
safeDescription.append("modify ");
}
safeDescription.append(resource.getSafeDescription()).append(".");
return safeDescription.toString();
})
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (Result.ResourceNotFound.equals(result.getResult())) { if (Result.ResourceNotFound.equals(result.getResult())) {
final Authorizable parent = getParentAuthorizable(); final Authorizable parent = getParentAuthorizable();
if (parent == null) { if (parent == null) {
throw new AccessDeniedException("Access is denied"); throw new AccessDeniedException("No applicable policies could be found.");
} else { } else {
parent.authorize(authorizer, action, user, resourceContext); // create a custom authorizable to override the safe description but still defer to the parent authorizable
final Authorizable parentProxy = new Authorizable() {
@Override
public Authorizable getParentAuthorizable() {
return parent.getParentAuthorizable();
}
@Override
public Resource getResource() {
final Resource parentResource = parent.getResource();
return new Resource() {
@Override
public String getIdentifier() {
return parentResource.getIdentifier();
}
@Override
public String getName() {
return parentResource.getName();
}
@Override
public String getSafeDescription() {
return resource.getSafeDescription();
}
};
}
};
parentProxy.authorize(authorizer, action, user, resourceContext);
} }
} else if (Result.Denied.equals(result.getResult())) { } else if (Result.Denied.equals(result.getResult())) {
throw new AccessDeniedException(result.getExplanation()); throw new AccessDeniedException(result.getExplanation());

View File

@ -16,7 +16,6 @@
*/ */
package org.apache.nifi.authorization; package org.apache.nifi.authorization;
import org.apache.nifi.authorization.MockPolicyBasedAuthorizer;
import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.apache.nifi.authorization.exception.AuthorizerCreationException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -42,6 +41,11 @@ public class TestAbstractPolicyBasedAuthorizer {
public String getName() { public String getName() {
return "resource1"; return "resource1";
} }
@Override
public String getSafeDescription() {
return "description1";
}
}; };
@Test @Test

View File

@ -447,8 +447,12 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
// convert any access controls on ports to the appropriate policies // convert any access controls on ports to the appropriate policies
for (PortDTO portDTO : ports) { for (PortDTO portDTO : ports) {
final boolean isInputPort = portDTO.getType() != null && portDTO.getType().equals("inputPort"); final Resource resource;
final Resource resource = ResourceFactory.getDataTransferResource(isInputPort, portDTO.getId(), portDTO.getName()); if (portDTO.getType() != null && portDTO.getType().equals("inputPort")) {
resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.InputPort, portDTO.getId(), portDTO.getName()));
} else {
resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.OutputPort, portDTO.getId(), portDTO.getName()));
}
if (portDTO.getUserAccessControl() != null) { if (portDTO.getUserAccessControl() != null) {
for (String userAccessControl : portDTO.getUserAccessControl()) { for (String userAccessControl : portDTO.getUserAccessControl()) {

View File

@ -358,7 +358,8 @@ public class FileAuthorizerTest {
assertEquals(2, user6Policies.get(ResourceType.SiteToSite.getValue()).size()); assertEquals(2, user6Policies.get(ResourceType.SiteToSite.getValue()).size());
assertTrue(user6Policies.get(ResourceType.SiteToSite.getValue()).contains(RequestAction.WRITE)); assertTrue(user6Policies.get(ResourceType.SiteToSite.getValue()).contains(RequestAction.WRITE));
final Resource inputPortResource = ResourceFactory.getDataTransferResource(true, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"); final Resource inputPortResource = ResourceFactory.getDataTransferResource(
ResourceFactory.getComponentResource(ResourceType.InputPort, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"));
final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE); final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE);
assertNotNull(inputPortPolicy); assertNotNull(inputPortPolicy);
assertEquals(1, inputPortPolicy.getUsers().size()); assertEquals(1, inputPortPolicy.getUsers().size());
@ -366,7 +367,8 @@ public class FileAuthorizerTest {
assertEquals(1, inputPortPolicy.getGroups().size()); assertEquals(1, inputPortPolicy.getGroups().size());
assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier())); assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier()));
final Resource outputPortResource = ResourceFactory.getDataTransferResource(false, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"); final Resource outputPortResource = ResourceFactory.getDataTransferResource(
ResourceFactory.getComponentResource(ResourceType.OutputPort, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"));
final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE); final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE);
assertNotNull(outputPortPolicy); assertNotNull(outputPortPolicy);
assertEquals(1, outputPortPolicy.getUsers().size()); assertEquals(1, outputPortPolicy.getUsers().size());
@ -432,7 +434,8 @@ public class FileAuthorizerTest {
final Group group1 = groups.iterator().next(); final Group group1 = groups.iterator().next();
assertEquals("group1", group1.getName()); assertEquals("group1", group1.getName());
final Resource inputPortResource = ResourceFactory.getDataTransferResource(true, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"); final Resource inputPortResource = ResourceFactory.getDataTransferResource(
ResourceFactory.getComponentResource(ResourceType.InputPort, "2f7d1606-b090-4be7-a592-a5b70fb55531", "TCP Input"));
final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE); final AccessPolicy inputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(inputPortResource.getIdentifier(), RequestAction.WRITE);
assertNotNull(inputPortPolicy); assertNotNull(inputPortPolicy);
assertEquals(1, inputPortPolicy.getUsers().size()); assertEquals(1, inputPortPolicy.getUsers().size());
@ -440,7 +443,8 @@ public class FileAuthorizerTest {
assertEquals(1, inputPortPolicy.getGroups().size()); assertEquals(1, inputPortPolicy.getGroups().size());
assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier())); assertTrue(inputPortPolicy.getGroups().contains(group1.getIdentifier()));
final Resource outputPortResource = ResourceFactory.getDataTransferResource(false, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"); final Resource outputPortResource = ResourceFactory.getDataTransferResource(
ResourceFactory.getComponentResource(ResourceType.OutputPort, "2f7d1606-b090-4be7-a592-a5b70fb55532", "TCP Output"));
final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE); final AccessPolicy outputPortPolicy = authorizer.getUsersAndAccessPolicies().getAccessPolicy(outputPortResource.getIdentifier(), RequestAction.WRITE);
assertNotNull(outputPortPolicy); assertNotNull(outputPortPolicy);
assertEquals(1, outputPortPolicy.getUsers().size()); assertEquals(1, outputPortPolicy.getUsers().size());

View File

@ -89,7 +89,7 @@ public class AccessPolicyAuthorizable implements Authorizable, EnforcePolicyPerm
@Override @Override
public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) { public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unknown user"); throw new AccessDeniedException("Unknown user.");
} }
final AuthorizationResult resourceResult = Authorizable.super.checkAuthorization(authorizer, action, user, resourceContext); final AuthorizationResult resourceResult = Authorizable.super.checkAuthorization(authorizer, action, user, resourceContext);
@ -105,7 +105,7 @@ public class AccessPolicyAuthorizable implements Authorizable, EnforcePolicyPerm
@Override @Override
public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException { public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unknown user"); throw new AccessDeniedException("Unknown user.");
} }
try { try {

View File

@ -62,7 +62,7 @@ public class DataAuthorizable implements Authorizable, EnforcePolicyPermissionsT
@Override @Override
public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) { public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
if (user == null) { if (user == null) {
return AuthorizationResult.denied("Unknown user"); return AuthorizationResult.denied("Unknown user.");
} }
AuthorizationResult result = null; AuthorizationResult result = null;
@ -100,7 +100,7 @@ public class DataAuthorizable implements Authorizable, EnforcePolicyPermissionsT
@Override @Override
public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException { public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unknown user"); throw new AccessDeniedException("Unknown user.");
} }
// calculate the dn chain // calculate the dn chain

View File

@ -32,6 +32,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Controller"; return "Controller";
} }
@Override
public String getSafeDescription() {
return "the controller";
}
}; };
private final static Resource FLOW_RESOURCE = new Resource() { private final static Resource FLOW_RESOURCE = new Resource() {
@ -44,6 +49,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "NiFi Flow"; return "NiFi Flow";
} }
@Override
public String getSafeDescription() {
return "the user interface";
}
}; };
private final static Resource POLICY_RESOURCE = new Resource() { private final static Resource POLICY_RESOURCE = new Resource() {
@ -54,7 +64,12 @@ public final class ResourceFactory {
@Override @Override
public String getName() { public String getName() {
return "Policy"; return "Policies for ";
}
@Override
public String getSafeDescription() {
return "the policies for ";
} }
}; };
@ -68,6 +83,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Counters"; return "Counters";
} }
@Override
public String getSafeDescription() {
return "counters";
}
}; };
private final static Resource PROVENANCE_RESOURCE = new Resource() { private final static Resource PROVENANCE_RESOURCE = new Resource() {
@ -80,6 +100,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Provenance"; return "Provenance";
} }
@Override
public String getSafeDescription() {
return "provenance";
}
}; };
private final static Resource DATA_RESOURCE = new Resource() { private final static Resource DATA_RESOURCE = new Resource() {
@ -90,7 +115,12 @@ public final class ResourceFactory {
@Override @Override
public String getName() { public String getName() {
return "Data"; return "Data for ";
}
@Override
public String getSafeDescription() {
return "the data for ";
} }
}; };
@ -104,6 +134,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Proxy User Requests"; return "Proxy User Requests";
} }
@Override
public String getSafeDescription() {
return "proxy requests on behalf of users";
}
}; };
private final static Resource RESOURCE_RESOURCE = new Resource() { private final static Resource RESOURCE_RESOURCE = new Resource() {
@ -116,6 +151,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "NiFi Resources"; return "NiFi Resources";
} }
@Override
public String getSafeDescription() {
return "resources";
}
}; };
private final static Resource SITE_TO_SITE_RESOURCE = new Resource() { private final static Resource SITE_TO_SITE_RESOURCE = new Resource() {
@ -128,6 +168,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Site to Site"; return "Site to Site";
} }
@Override
public String getSafeDescription() {
return "site-to-site details";
}
}; };
private final static Resource SYSTEM_RESOURCE = new Resource() { private final static Resource SYSTEM_RESOURCE = new Resource() {
@ -140,6 +185,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "System"; return "System";
} }
@Override
public String getSafeDescription() {
return "system diagnostics";
}
}; };
private final static Resource RESTRICTED_COMPONENTS_RESOURCE = new Resource() { private final static Resource RESTRICTED_COMPONENTS_RESOURCE = new Resource() {
@ -152,6 +202,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Restricted Components"; return "Restricted Components";
} }
@Override
public String getSafeDescription() {
return "restricted components";
}
}; };
private final static Resource TENANT_RESOURCE = new Resource() { private final static Resource TENANT_RESOURCE = new Resource() {
@ -164,6 +219,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Tenant"; return "Tenant";
} }
@Override
public String getSafeDescription() {
return "users/user groups";
}
}; };
private final static Resource POLICIES_RESOURCE = new Resource() { private final static Resource POLICIES_RESOURCE = new Resource() {
@ -177,6 +237,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Access Policies"; return "Access Policies";
} }
@Override
public String getSafeDescription() {
return "policies";
}
}; };
/** /**
@ -271,30 +336,6 @@ public final class ResourceFactory {
return TENANT_RESOURCE; return TENANT_RESOURCE;
} }
/**
* Gets a Resource for performing transferring data to a port.
*
* @param isInputPort Whether this port is an input port or an output port
* @param identifier The identifier of the component being accessed
* @param name The name of the component being accessed
* @return The resource
*/
public static Resource getDataTransferResource(final boolean isInputPort, final String identifier, final String name) {
Objects.requireNonNull(identifier, "The component identifier must be specified.");
return new Resource() {
@Override
public String getIdentifier() {
return String.format("%s/%s/%s", ResourceType.DataTransfer.getValue(), isInputPort ? "input-ports" : "output-ports", identifier);
}
@Override
public String getName() {
return name;
}
};
}
/** /**
* Gets a Resource for performing transferring data to a port. * Gets a Resource for performing transferring data to a port.
* *
@ -314,6 +355,11 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return "Transfer data to " + resource.getName(); return "Transfer data to " + resource.getName();
} }
@Override
public String getSafeDescription() {
return "data transfers to " + resource.getSafeDescription();
}
}; };
} }
@ -342,7 +388,12 @@ public final class ResourceFactory {
@Override @Override
public String getName() { public String getName() {
return "Policies for " + resource.getName(); return POLICY_RESOURCE.getName() + resource.getName();
}
@Override
public String getSafeDescription() {
return POLICY_RESOURCE.getSafeDescription() + resource.getSafeDescription();
} }
}; };
} }
@ -369,6 +420,47 @@ public final class ResourceFactory {
public String getName() { public String getName() {
return name; return name;
} }
@Override
public String getSafeDescription() {
final String componentType;
switch (resourceType) {
case ControllerService:
componentType = "Controller Service";
break;
case ProcessGroup:
componentType = "Process Group";
break;
case Template:
componentType = "Template";
break;
case Funnel:
componentType = "Funnel";
break;
case InputPort:
componentType = "Input Port";
break;
case OutputPort:
componentType = "Output Port";
break;
case Processor:
componentType = "Processor";
break;
case RemoteProcessGroup:
componentType = "Remote Process Group";
break;
case ReportingTask:
componentType = "Reporting Task";
break;
case Label:
componentType = "Label";
break;
default:
componentType = "Component";
break;
}
return componentType + " with ID " + identifier;
}
}; };
} }
@ -387,7 +479,12 @@ public final class ResourceFactory {
@Override @Override
public String getName() { public String getName() {
return "Data for " + resource.getName(); return DATA_RESOURCE.getName() + resource.getName();
}
@Override
public String getSafeDescription() {
return DATA_RESOURCE.getSafeDescription() + resource.getSafeDescription();
} }
}; };
} }

View File

@ -136,6 +136,11 @@ public final class StandardConnection implements Connection {
return name; return name;
} }
@Override
public String getSafeDescription() {
return "Connection " + StandardConnection.this.getIdentifier();
}
}; };
} }
@ -172,7 +177,7 @@ public final class StandardConnection implements Connection {
@Override @Override
public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) { public AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
if (user == null) { if (user == null) {
return AuthorizationResult.denied("Unknown user"); return AuthorizationResult.denied("Unknown user.");
} }
// check the source // check the source
@ -188,7 +193,7 @@ public final class StandardConnection implements Connection {
@Override @Override
public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException { public void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unknown user"); throw new AccessDeniedException("Unknown user.");
} }
getSourceAuthorizable().authorize(authorizer, action, user, resourceContext); getSourceAuthorizable().authorize(authorizer, action, user, resourceContext);

View File

@ -963,6 +963,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public String getName() { public String getName() {
return resourceIdentifier; return resourceIdentifier;
} }
@Override
public String getSafeDescription() {
return "User " + userId;
}
}, },
() -> userDAO.deleteUser(userId), () -> userDAO.deleteUser(userId),
false, // no user specific policies to remove false, // no user specific policies to remove
@ -993,6 +998,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public String getName() { public String getName() {
return resourceIdentifier; return resourceIdentifier;
} }
@Override
public String getSafeDescription() {
return "User Group " + userGroupId;
}
}, },
() -> userGroupDAO.deleteUserGroup(userGroupId), () -> userGroupDAO.deleteUserGroup(userGroupId),
false, // no user group specific policies to remove false, // no user group specific policies to remove
@ -1020,6 +1030,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public String getName() { public String getName() {
return accessPolicy.getResource(); return accessPolicy.getResource();
} }
@Override
public String getSafeDescription() {
return "Policy " + accessPolicyId;
}
}, },
() -> accessPolicyDAO.deleteAccessPolicy(accessPolicyId), () -> accessPolicyDAO.deleteAccessPolicy(accessPolicyId),
false, // no need to clean up any policies as it's already been removed above false, // no need to clean up any policies as it's already been removed above
@ -2514,6 +2529,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
.accessAttempt(false) .accessAttempt(false)
.action(RequestAction.WRITE) .action(RequestAction.WRITE)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to retrieve port details.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
@ -2680,6 +2696,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
public String getName() { public String getName() {
return resource; return resource;
} }
@Override
public String getSafeDescription() {
return "Policy " + resource;
}
}; };
} }
}; };
@ -3100,7 +3121,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return entityFactory.createStatusHistoryEntity(dto, permissions); return entityFactory.createStatusHistoryEntity(dto, permissions);
} }
private boolean authorizeAction(final Action action) { private AuthorizationResult authorizeAction(final Action action) {
final String sourceId = action.getSourceId(); final String sourceId = action.getSourceId();
final Component type = action.getSourceType(); final Component type = action.getSourceType();
@ -3149,12 +3170,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
} }
} catch (final ResourceNotFoundException e) { } catch (final ResourceNotFoundException e) {
// if the underlying component is gone, disallow // if the underlying component is gone, disallow
return false; return AuthorizationResult.denied("The component of this action is no longer in the data flow.");
} }
// perform the authorization // perform the authorization
final AuthorizationResult result = authorizable.checkAuthorization(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); return authorizable.checkAuthorization(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
return Result.Approved.equals(result.getResult());
} }
@Override @Override
@ -3178,7 +3198,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
if (history.getActions() != null) { if (history.getActions() != null) {
final List<ActionEntity> actionEntities = new ArrayList<>(); final List<ActionEntity> actionEntities = new ArrayList<>();
for (final Action action : history.getActions()) { for (final Action action : history.getActions()) {
actionEntities.add(entityFactory.createActionEntity(dtoFactory.createActionDto(action), authorizeAction(action))); final AuthorizationResult result = authorizeAction(action);
actionEntities.add(entityFactory.createActionEntity(dtoFactory.createActionDto(action), Result.Approved.equals(result.getResult())));
} }
historyDto.setActions(actionEntities); historyDto.setActions(actionEntities);
} }
@ -3197,9 +3218,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
throw new ResourceNotFoundException(String.format("Unable to find action with id '%s'.", actionId)); throw new ResourceNotFoundException(String.format("Unable to find action with id '%s'.", actionId));
} }
final boolean authorized = authorizeAction(action); final AuthorizationResult result = authorizeAction(action);
final boolean authorized = Result.Approved.equals(result.getResult());
if (!authorized) { if (!authorized) {
throw new AccessDeniedException("Access is denied."); throw new AccessDeniedException(result.getExplanation());
} }
// return the action // return the action

View File

@ -115,12 +115,12 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to view the user interface.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
}); });
} }

View File

@ -252,7 +252,7 @@ public class AccessResource extends ApplicationResource {
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unable to determine user details."); throw new AccessDeniedException("No user authenticated in the request.");
} }
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity()); final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
@ -297,7 +297,7 @@ public class AccessResource extends ApplicationResource {
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user == null) { if (user == null) {
throw new AccessDeniedException("Unable to determine user details."); throw new AccessDeniedException("No user authenticated in the request.");
} }
final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity()); final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());

View File

@ -112,12 +112,23 @@ public class ControllerResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(action) .action(action)
.userContext(userContext) .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(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -98,12 +98,23 @@ public class CountersResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(action) .action(action)
.userContext(userContext) .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(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -218,12 +218,12 @@ public class FlowResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to view the user interface.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -108,12 +108,12 @@ public class ProvenanceResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to query provenance.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -78,12 +78,12 @@ public class ResourceResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to retrieve resources.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -116,12 +116,12 @@ public class SiteToSiteResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to retrieve site to site details.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -81,12 +81,12 @@ public class SystemDiagnosticsResource extends ApplicationResource {
.accessAttempt(true) .accessAttempt(true)
.action(RequestAction.READ) .action(RequestAction.READ)
.userContext(userContext) .userContext(userContext)
.explanationSupplier(() -> "Unable to view system diagnostics.")
.build(); .build();
final AuthorizationResult result = authorizer.authorize(request); final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) { if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied"; throw new AccessDeniedException(result.getExplanation());
throw new AccessDeniedException(message);
} }
} }

View File

@ -58,13 +58,16 @@ public class AccessDeniedExceptionMapper implements ExceptionMapper<AccessDenied
identity = user.getIdentity(); identity = user.getIdentity();
} }
logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", identity, status)); logger.info(String.format("%s does not have permission to access the requested resource. %s Returning %s response.", identity, exception.getMessage(), status));
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, exception); logger.debug(StringUtils.EMPTY, exception);
} }
return Response.status(status).entity("Unable to perform the desired action due to insufficient permissions. Contact the system administrator.").type("text/plain").build(); return Response.status(status)
.entity(String.format("%s Contact the system administrator.", exception.getMessage()))
.type("text/plain")
.build();
} }
} }

View File

@ -1241,7 +1241,7 @@ public class ControllerFacade implements Authorizable {
private AuthorizationResult checkAuthorizationForReplay(final ProvenanceEventRecord event) { private AuthorizationResult checkAuthorizationForReplay(final ProvenanceEventRecord event) {
// if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it` // if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it`
if (event.getSourceQueueIdentifier() == null) { if (event.getSourceQueueIdentifier() == null) {
return AuthorizationResult.denied(); return AuthorizationResult.denied("The connection id in the provenance event is unknown.");
} }
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();
@ -1272,7 +1272,7 @@ public class ControllerFacade implements Authorizable {
private void authorizeReplay(final ProvenanceEventRecord event) { private void authorizeReplay(final ProvenanceEventRecord event) {
// if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it` // if the connection id isn't specified, then the replay wouldn't be available anyways and we have nothing to authorize against so deny it`
if (event.getSourceQueueIdentifier() == null) { if (event.getSourceQueueIdentifier() == null) {
throw new AccessDeniedException("The connection id is unknown."); throw new AccessDeniedException("The connection id in the provenance event is unknown.");
} }
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();

View File

@ -514,7 +514,7 @@ nf.Common = (function () {
if (xhr.status === 401) { if (xhr.status === 401) {
$('#message-title').text('Unauthorized'); $('#message-title').text('Unauthorized');
} else if (xhr.status === 403) { } else if (xhr.status === 403) {
$('#message-title').text('Access Denied'); $('#message-title').text('Insufficient Permissions');
} else if (xhr.status === 409) { } else if (xhr.status === 409) {
$('#message-title').text('Invalid State'); $('#message-title').text('Invalid State');
} else { } else {
@ -535,12 +535,17 @@ nf.Common = (function () {
return; return;
} }
// status code 400, 403, 404, and 409 are expected response codes for common errors. // status code 400, 404, and 409 are expected response codes for common errors.
if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404 || xhr.status === 409 || xhr.status === 503) { if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409 || xhr.status === 503) {
nf.Dialog.showOkDialog({ nf.Dialog.showOkDialog({
headerText: 'Error', headerText: 'Error',
dialogContent: nf.Common.escapeHtml(xhr.responseText) dialogContent: nf.Common.escapeHtml(xhr.responseText)
}); });
} else if (xhr.status === 403) {
nf.Dialog.showOkDialog({
headerText: 'Insufficient Permissions',
dialogContent: nf.Common.escapeHtml(xhr.responseText)
});
} else { } else {
if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) { if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) {
var content = 'Please ensure the application is running and check the logs for any errors.'; var content = 'Please ensure the application is running and check the logs for any errors.';

View File

@ -2384,14 +2384,14 @@ public class PersistentProvenanceRepository implements ProvenanceRepository {
} }
if (user == null) { if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided"); throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided in the lineage request.");
} }
if (userId == null || userId.equals(user.getIdentity())) { if (userId == null || userId.equals(user.getIdentity())) {
return submission; return submission;
} }
throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request"); throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request.");
} }
@Override @Override
@ -2405,14 +2405,14 @@ public class PersistentProvenanceRepository implements ProvenanceRepository {
} }
if (user == null) { if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided"); throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided in the provenance request.");
} }
if (userId == null || userId.equals(user.getIdentity())) { if (userId == null || userId.equals(user.getIdentity())) {
return submission; return submission;
} }
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request"); throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request.");
} }
@Override @Override

View File

@ -460,14 +460,14 @@ public class VolatileProvenanceRepository implements ProvenanceRepository {
} }
if (user == null) { if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided"); throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided in the provenance request.");
} }
if (userId == null || userId.equals(user.getIdentity())) { if (userId == null || userId.equals(user.getIdentity())) {
return submission; return submission;
} }
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request"); throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request.");
} }
public Lineage computeLineage(final String flowFileUUID, final NiFiUser user) throws IOException { public Lineage computeLineage(final String flowFileUUID, final NiFiUser user) throws IOException {
@ -520,14 +520,14 @@ public class VolatileProvenanceRepository implements ProvenanceRepository {
} }
if (user == null) { if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided"); throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided in the lineage request.");
} }
if (userId == null || userId.equals(user.getIdentity())) { if (userId == null || userId.equals(user.getIdentity())) {
return submission; return submission;
} }
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request"); throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request.");
} }
public Lineage expandSpawnEventParents(String identifier) throws IOException { public Lineage expandSpawnEventParents(String identifier) throws IOException {

View File

@ -179,13 +179,13 @@ public class RangerNiFiAuthorizer implements Authorizer {
final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier()); final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier());
if (doesPolicyExist) { if (doesPolicyExist) {
// a policy does exist for the resource so we were really denied access here
final String reason = result == null ? null : result.getReason(); final String reason = result == null ? null : result.getReason();
if (reason == null) { if (reason != null) {
return AuthorizationResult.denied(); logger.debug(String.format("Unable to authorize %s due to %s", identity, reason));
} else {
return AuthorizationResult.denied(result.getReason());
} }
// a policy does exist for the resource so we were really denied access here
return AuthorizationResult.denied(request.getExplanationSupplier().get());
} else { } else {
// a policy doesn't exist so return resource not found so NiFi can work back up the resource hierarchy // a policy doesn't exist so return resource not found so NiFi can work back up the resource hierarchy
return AuthorizationResult.resourceNotFound(); return AuthorizationResult.resourceNotFound();

View File

@ -467,6 +467,11 @@ public class TestRangerNiFiAuthorizer {
public String getName() { public String getName() {
return "/system"; return "/system";
} }
@Override
public String getSafeDescription() {
return "system";
}
}) })
.action(RequestAction.WRITE) .action(RequestAction.WRITE)
.identity("admin") .identity("admin")
@ -526,6 +531,11 @@ public class TestRangerNiFiAuthorizer {
public String getName() { public String getName() {
return name; return name;
} }
@Override
public String getSafeDescription() {
return name;
}
} }
/** /**