mirror of https://github.com/apache/nifi.git
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:
parent
41189b055f
commit
b1c9f0e764
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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.';
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue