http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index 5d41c2c..b72d72c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -19,10 +19,6 @@ package org.apache.nifi.web.controller; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.AuthorizationRequest; -import org.apache.nifi.authorization.AuthorizationResult; -import org.apache.nifi.authorization.AuthorizationResult.Result; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.Resource; @@ -31,6 +27,7 @@ import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.resource.ResourceType; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.cluster.coordination.ClusterCoordinator; import org.apache.nifi.cluster.coordination.node.NodeConnectionState; import org.apache.nifi.cluster.protocol.NodeIdentifier; @@ -116,7 +113,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; import java.text.Collator; @@ -803,7 +799,7 @@ public class ControllerFacade implements Authorizable { // add each processor for (final ProcessorNode processor : root.findAllProcessors()) { resources.add(ResourceFactory.getComponentResource(ResourceType.Processor, processor.getIdentifier(), processor.getName())); - resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.Processor, processor.getIdentifier(), processor.getName())); + resources.add(ResourceFactory.getProvenanceEventResource(processor.getResource())); } // add each connection @@ -820,25 +816,25 @@ public class ControllerFacade implements Authorizable { // add each process group for (final ProcessGroup processGroup : root.findAllProcessGroups()) { resources.add(ResourceFactory.getComponentResource(ResourceType.ProcessGroup, processGroup.getIdentifier(), processGroup.getName())); - resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.ProcessGroup, processGroup.getIdentifier(), processGroup.getName())); + resources.add(ResourceFactory.getProvenanceEventResource(processGroup.getResource())); } // add each remote process group for (final RemoteProcessGroup remoteProcessGroup : root.findAllRemoteProcessGroups()) { resources.add(ResourceFactory.getComponentResource(ResourceType.RemoteProcessGroup, remoteProcessGroup.getIdentifier(), remoteProcessGroup.getName())); - resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.RemoteProcessGroup, remoteProcessGroup.getIdentifier(), remoteProcessGroup.getName())); + resources.add(ResourceFactory.getProvenanceEventResource(remoteProcessGroup.getResource())); } // add each input port for (final Port inputPort : root.findAllInputPorts()) { resources.add(ResourceFactory.getComponentResource(ResourceType.InputPort, inputPort.getIdentifier(), inputPort.getName())); - resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.InputPort, inputPort.getIdentifier(), inputPort.getName())); + resources.add(ResourceFactory.getProvenanceEventResource(inputPort.getResource())); } // add each output port for (final Port outputPort : root.findAllOutputPorts()) { resources.add(ResourceFactory.getComponentResource(ResourceType.OutputPort, outputPort.getIdentifier(), outputPort.getName())); - resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.OutputPort, outputPort.getIdentifier(), outputPort.getName())); + resources.add(ResourceFactory.getProvenanceEventResource(outputPort.getResource())); } // add each controller service @@ -943,7 +939,7 @@ public class ControllerFacade implements Authorizable { // submit the query to the provenance repository final ProvenanceEventRepository provenanceRepository = flowController.getProvenanceRepository(); - final QuerySubmission querySubmission = provenanceRepository.submitQuery(query); + final QuerySubmission querySubmission = provenanceRepository.submitQuery(query, NiFiUserUtils.getNiFiUser()); // return the query with the results populated at this point return getProvenanceQuery(querySubmission.getQueryIdentifier()); @@ -959,7 +955,7 @@ public class ControllerFacade implements Authorizable { try { // get the query to the provenance repository final ProvenanceEventRepository provenanceRepository = flowController.getProvenanceRepository(); - final QuerySubmission querySubmission = provenanceRepository.retrieveQuerySubmission(provenanceId); + final QuerySubmission querySubmission = provenanceRepository.retrieveQuerySubmission(provenanceId, NiFiUserUtils.getNiFiUser()); // ensure the query results could be found if (querySubmission == null) { @@ -1056,13 +1052,13 @@ public class ControllerFacade implements Authorizable { // submit the event if (LineageRequestType.FLOWFILE.equals(requestDto.getLineageRequestType())) { // submit uuid - result = provenanceRepository.submitLineageComputation(requestDto.getUuid()); + result = provenanceRepository.submitLineageComputation(requestDto.getUuid(), NiFiUserUtils.getNiFiUser()); } else { // submit event... (parents or children) if (LineageRequestType.PARENTS.equals(requestDto.getLineageRequestType())) { - result = provenanceRepository.submitExpandParents(requestDto.getEventId()); + result = provenanceRepository.submitExpandParents(requestDto.getEventId(), NiFiUserUtils.getNiFiUser()); } else { - result = provenanceRepository.submitExpandChildren(requestDto.getEventId()); + result = provenanceRepository.submitExpandChildren(requestDto.getEventId(), NiFiUserUtils.getNiFiUser()); } } @@ -1078,7 +1074,7 @@ public class ControllerFacade implements Authorizable { public LineageDTO getLineage(final String lineageId) { // get the query to the provenance repository final ProvenanceEventRepository provenanceRepository = flowController.getProvenanceRepository(); - final ComputeLineageSubmission computeLineageSubmission = provenanceRepository.retrieveLineageSubmission(lineageId); + final ComputeLineageSubmission computeLineageSubmission = provenanceRepository.retrieveLineageSubmission(lineageId, NiFiUserUtils.getNiFiUser()); // ensure the submission was found if (computeLineageSubmission == null) { @@ -1096,7 +1092,7 @@ public class ControllerFacade implements Authorizable { public void deleteProvenanceQuery(final String provenanceId) { // get the query to the provenance repository final ProvenanceEventRepository provenanceRepository = flowController.getProvenanceRepository(); - final QuerySubmission querySubmission = provenanceRepository.retrieveQuerySubmission(provenanceId); + final QuerySubmission querySubmission = provenanceRepository.retrieveQuerySubmission(provenanceId, NiFiUserUtils.getNiFiUser()); if (querySubmission != null) { querySubmission.cancel(); } @@ -1110,7 +1106,7 @@ public class ControllerFacade implements Authorizable { public void deleteLineage(final String lineageId) { // get the query to the provenance repository final ProvenanceEventRepository provenanceRepository = flowController.getProvenanceRepository(); - final ComputeLineageSubmission computeLineageSubmission = provenanceRepository.retrieveLineageSubmission(lineageId); + final ComputeLineageSubmission computeLineageSubmission = provenanceRepository.retrieveLineageSubmission(lineageId, NiFiUserUtils.getNiFiUser()); if (computeLineageSubmission != null) { computeLineageSubmission.cancel(); } @@ -1129,7 +1125,7 @@ public class ControllerFacade implements Authorizable { final NiFiUser user = NiFiUserUtils.getNiFiUser(); // get the event in order to get the filename - final ProvenanceEventRecord event = flowController.getProvenanceRepository().getEvent(eventId); + final ProvenanceEventRecord event = flowController.getProvenanceRepository().getEvent(eventId, NiFiUserUtils.getNiFiUser()); if (event == null) { throw new ResourceNotFoundException("Unable to find the specified event."); } @@ -1145,54 +1141,18 @@ public class ControllerFacade implements Authorizable { // calculate the dn chain final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user); dnChain.forEach(identity -> { - final String rootGroupId = flowController.getRootGroupId(); - final ProcessGroup rootGroup = flowController.getGroup(rootGroupId); - - final Resource eventResource; - if (rootGroupId.equals(event.getComponentId())) { - eventResource = ResourceFactory.getComponentProvenanceResource(ResourceType.ProcessGroup, rootGroup.getIdentifier(), rootGroup.getName()); - } else { - final Connectable connectable = rootGroup.findConnectable(event.getComponentId()); - - if (connectable == null) { - throw new AccessDeniedException("The component that generated this event is no longer part of the data flow. Unable to determine access policy."); - } - - switch (connectable.getConnectableType()) { - case PROCESSOR: - eventResource = ResourceFactory.getComponentProvenanceResource(ResourceType.Processor, connectable.getIdentifier(), connectable.getName()); - break; - case INPUT_PORT: - case REMOTE_INPUT_PORT: - eventResource = ResourceFactory.getComponentProvenanceResource(ResourceType.InputPort, connectable.getIdentifier(), connectable.getName()); - break; - case OUTPUT_PORT: - case REMOTE_OUTPUT_PORT: - eventResource = ResourceFactory.getComponentProvenanceResource(ResourceType.OutputPort, connectable.getIdentifier(), connectable.getName()); - break; - case FUNNEL: - eventResource = ResourceFactory.getComponentProvenanceResource(ResourceType.Funnel, connectable.getIdentifier(), connectable.getName()); - break; - default: - throw new WebApplicationException(Response.serverError().entity("An unexpected type of component generated this event.").build()); + final Authorizable eventAuthorizable = flowController.createProvenanceAuthorizable(event.getComponentId()); + final NiFiUser chainUser = new StandardNiFiUser(identity) { + private static final long serialVersionUID = 7589311627013017356L; + + @Override + public boolean isAnonymous() { + // allow current user to drive anonymous flag as anonymous users are never chained... supports single user case + return user.isAnonymous(); } - } + }; - // build the request - final AuthorizationRequest request = new AuthorizationRequest.Builder() - .identity(identity) - .anonymous(user.isAnonymous()) // allow current user to drive anonymous flag as anonymous users are never chained... supports single user case - .accessAttempt(false) - .action(RequestAction.READ) - .resource(eventResource) - .eventAttributes(attributes) - .build(); - - // perform the authorization - final AuthorizationResult result = authorizer.authorize(request); - if (!Result.Approved.equals(result.getResult())) { - throw new AccessDeniedException(result.getExplanation()); - } + eventAuthorizable.authorize(authorizer, RequestAction.READ, chainUser); }); // get the filename and fall back to the identifier (should never happen) @@ -1229,13 +1189,13 @@ public class ControllerFacade implements Authorizable { } // lookup the original event - final ProvenanceEventRecord originalEvent = flowController.getProvenanceRepository().getEvent(eventId); + final ProvenanceEventRecord originalEvent = flowController.getProvenanceRepository().getEvent(eventId, NiFiUserUtils.getNiFiUser()); if (originalEvent == null) { throw new ResourceNotFoundException("Unable to find the specified event."); } // replay the flow file - final ProvenanceEventRecord event = flowController.replayFlowFile(originalEvent, user.getIdentity()); + final ProvenanceEventRecord event = flowController.replayFlowFile(originalEvent, user); // convert the event record return createProvenanceEventDto(event); @@ -1252,7 +1212,7 @@ public class ControllerFacade implements Authorizable { */ public ProvenanceEventDTO getProvenanceEvent(final Long eventId) { try { - final ProvenanceEventRecord event = flowController.getProvenanceRepository().getEvent(eventId); + final ProvenanceEventRecord event = flowController.getProvenanceRepository().getEvent(eventId, NiFiUserUtils.getNiFiUser()); if (event == null) { throw new ResourceNotFoundException("Unable to find the specified event."); }
http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java index e16dd05..1a2669b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java @@ -298,7 +298,7 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO } // ensure the user has write access to the source component - source.authorize(authorizer, RequestAction.WRITE); + source.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); // find the destination final Connectable destination; @@ -324,7 +324,7 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO } // ensure the user has write access to the source component - destination.authorize(authorizer, RequestAction.WRITE); + destination.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser()); // determine the relationships final Set<String> relationships = new HashSet<>(); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy index 7b5f594..87dbf86 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy @@ -20,6 +20,7 @@ import org.apache.nifi.authorization.* import org.apache.nifi.authorization.resource.Authorizable import org.apache.nifi.authorization.resource.ResourceFactory import org.apache.nifi.authorization.user.NiFiUser +import org.apache.nifi.authorization.user.StandardNiFiUser import org.apache.nifi.authorization.user.NiFiUserDetails import org.apache.nifi.controller.service.ControllerServiceProvider import org.apache.nifi.web.api.dto.* @@ -39,7 +40,7 @@ import spock.lang.Unroll class StandardNiFiServiceFacadeSpec extends Specification { def setup() { - final NiFiUser user = new NiFiUser("nifi-user"); + final NiFiUser user = new StandardNiFiUser("nifi-user"); final NiFiAuthenticationToken auth = new NiFiAuthenticationToken(new NiFiUserDetails(user)); SecurityContextHolder.getContext().setAuthentication(auth); } @@ -867,17 +868,17 @@ class StandardNiFiServiceFacadeSpec extends Specification { } @Override - boolean isAuthorized(Authorizer authorzr, RequestAction action) { + boolean isAuthorized(Authorizer authorzr, RequestAction action, NiFiUser user) { return isAuthorized } @Override - AuthorizationResult checkAuthorization(Authorizer authorzr, RequestAction action) { + AuthorizationResult checkAuthorization(Authorizer authorzr, RequestAction action, NiFiUser user) { return authorizationResult } @Override - void authorize(Authorizer authorzr, RequestAction action) throws AccessDeniedException { + void authorize(Authorizer authorzr, RequestAction action, NiFiUser user) throws AccessDeniedException { if (!isAuthorized) { throw new AccessDeniedException("test exception, access denied") } http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/test/java/org/apache/nifi/web/revision/TestNaiveRevisionManager.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/test/java/org/apache/nifi/web/revision/TestNaiveRevisionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/test/java/org/apache/nifi/web/revision/TestNaiveRevisionManager.java index e3148d3..a4e17e8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/test/java/org/apache/nifi/web/revision/TestNaiveRevisionManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/test/java/org/apache/nifi/web/revision/TestNaiveRevisionManager.java @@ -39,6 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.web.FlowModification; import org.apache.nifi.web.InvalidRevisionException; import org.apache.nifi.web.Revision; @@ -49,7 +50,7 @@ import org.junit.Test; public class TestNaiveRevisionManager { private static final String CLIENT_1 = "client-1"; private static final String COMPONENT_1 = "component-1"; - private static final NiFiUser USER_1 = new NiFiUser("user-1"); + private static final NiFiUser USER_1 = new StandardNiFiUser("user-1"); private RevisionUpdate<Object> components(final Revision revision) { return new StandardRevisionUpdate<Object>(null, new FlowModification(revision, null)); @@ -302,7 +303,7 @@ public class TestNaiveRevisionManager { final RevisionClaim firstClaim = revisionManager.requestClaim(firstRevision, USER_1); assertNotNull(firstClaim); - final NiFiUser user2 = new NiFiUser("user-2"); + final NiFiUser user2 = new StandardNiFiUser("user-2"); try { revisionManager.updateRevision(firstClaim, user2, () -> null); Assert.fail("Expected updateRevision to fail with a different user but it succeeded"); @@ -318,7 +319,7 @@ public class TestNaiveRevisionManager { final RevisionClaim firstClaim = revisionManager.requestClaim(firstRevision, USER_1); assertNotNull(firstClaim); - final NiFiUser user2 = new NiFiUser("user-2"); + final NiFiUser user2 = new StandardNiFiUser("user-2"); try { revisionManager.deleteRevision(firstClaim, user2, () -> null); Assert.fail("Expected deleteRevision to fail with a different user but it succeeded"); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java index b1a9a66..da9c52e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java @@ -16,14 +16,14 @@ */ package org.apache.nifi.web.security.anonymous; -import org.apache.nifi.authorization.user.NiFiUser; +import javax.servlet.http.HttpServletRequest; + import org.apache.nifi.authorization.user.NiFiUserDetails; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.web.security.token.NiFiAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; -import javax.servlet.http.HttpServletRequest; - public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { private static final String ANONYMOUS_KEY = "anonymousNifiKey"; @@ -34,7 +34,7 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { @Override protected Authentication createAuthentication(HttpServletRequest request) { - return new NiFiAuthenticationToken(new NiFiUserDetails(NiFiUser.ANONYMOUS)); + return new NiFiAuthenticationToken(new NiFiUserDetails(StandardNiFiUser.ANONYMOUS)); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProvider.java index 7634123..f68935e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationProvider.java @@ -16,15 +16,17 @@ */ package org.apache.nifi.web.security.jwt; -import io.jsonwebtoken.JwtException; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserDetails; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.token.NiFiAuthenticationToken; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import io.jsonwebtoken.JwtException; + /** * */ @@ -42,7 +44,7 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { try { final String jwtPrincipal = jwtService.getAuthenticationFromToken(request.getToken()); - final NiFiUser user = new NiFiUser(jwtPrincipal); + final NiFiUser user = new StandardNiFiUser(jwtPrincipal); return new NiFiAuthenticationToken(new NiFiUserDetails(user)); } catch (JwtException e) { throw new InvalidAuthenticationException(e.getMessage(), e); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationProvider.java index 8f2712c..b80a97e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationProvider.java @@ -18,6 +18,7 @@ package org.apache.nifi.web.security.otp; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserDetails; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.token.NiFiAuthenticationToken; import org.springframework.security.authentication.AuthenticationProvider; @@ -46,7 +47,7 @@ public class OtpAuthenticationProvider implements AuthenticationProvider { } else { otpPrincipal = otpService.getAuthenticationFromUiExtensionToken(request.getToken()); } - final NiFiUser user = new NiFiUser(otpPrincipal); + final NiFiUser user = new StandardNiFiUser(otpPrincipal); return new NiFiAuthenticationToken(new NiFiUserDetails(user)); } catch (OtpAuthenticationException e) { throw new InvalidAuthenticationException(e.getMessage(), e); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationToken.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationToken.java index f7964f5..564ac5f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationToken.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthenticationToken.java @@ -47,4 +47,9 @@ public class NiFiAuthenticationToken extends AbstractAuthenticationToken { public final void setAuthenticated(boolean authenticated) { throw new IllegalArgumentException("Cannot change the authenticated state."); } + + @Override + public String toString() { + return nifiUserDetails.getUsername(); + } } http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java index 43d6958..b72d5e1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationProvider.java @@ -26,6 +26,7 @@ import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserDetails; +import org.apache.nifi.authorization.user.StandardNiFiUser; import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.UntrustedProxyException; @@ -64,7 +65,7 @@ public class X509AuthenticationProvider implements AuthenticationProvider { } if (StringUtils.isBlank(request.getProxiedEntitiesChain())) { - return new NiFiAuthenticationToken(new NiFiUserDetails(new NiFiUser(authenticationResponse.getIdentity(), authenticationResponse.getUsername(), null))); + return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(authenticationResponse.getIdentity(), authenticationResponse.getUsername(), null))); } else { // build the entire proxy chain if applicable - <end-user><proxy1><proxy2> final List<String> proxyChain = new ArrayList<>(ProxiedEntitiesUtils.tokenizeProxiedEntitiesChain(request.getProxiedEntitiesChain())); @@ -91,7 +92,7 @@ public class X509AuthenticationProvider implements AuthenticationProvider { } } - proxy = new NiFiUser(chainIter.previous(), proxy); + proxy = new StandardNiFiUser(chainIter.previous(), proxy); } return new NiFiAuthenticationToken(new NiFiUserDetails(proxy)); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js index 7c27746..04a2204 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js @@ -145,9 +145,9 @@ nf.ProvenanceLineage = (function () { */ var getLineage = function (lineage) { var url = lineage.uri; - if (nf.Common.isDefinedAndNotNull(lineage.clusterNodeId)) { + if (nf.Common.isDefinedAndNotNull(lineage.request.clusterNodeId)) { url += '?' + $.param({ - clusterNodeId: lineage.clusterNodeId + clusterNodeId: lineage.request.clusterNodeId }); } @@ -166,9 +166,9 @@ nf.ProvenanceLineage = (function () { */ var cancelLineage = function (lineage) { var url = lineage.uri; - if (nf.Common.isDefinedAndNotNull(lineage.clusterNodeId)) { + if (nf.Common.isDefinedAndNotNull(lineage.request.clusterNodeId)) { url += '?' + $.param({ - clusterNodeId: lineage.clusterNodeId + clusterNodeId: lineage.request.clusterNodeId }); } http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js index ca1a83e..f024866 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js @@ -384,7 +384,7 @@ nf.ProvenanceTable = (function () { startTime = config.defaultStartTime; $('#provenance-search-start-time').val(startTime); } - search['startDate'] = startDate + ' ' + startTime; + search['startDate'] = startDate + ' ' + startTime + ' ' + $('.timezone:first').text(); } // extract the end date time @@ -395,7 +395,7 @@ nf.ProvenanceTable = (function () { endTime = config.defaultEndTime; $('#provenance-search-end-time').val(endTime); } - search['endDate'] = endDate + ' ' + endTime; + search['endDate'] = endDate + ' ' + endTime + ' ' + $('.timezone:first').text(); } // extract the min/max file size http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js index e1891a2..a04aad7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js @@ -29,7 +29,7 @@ nf.Provenance = (function () { */ var config = { urls: { - cluster: '../nifi-api/controller/cluster', + flowConfig: '../nifi-api/flow/config', banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', authorities: '../nifi-api/flow/authorities' @@ -45,23 +45,12 @@ nf.Provenance = (function () { * Determines if this NiFi is clustered. */ var detectedCluster = function () { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'HEAD', - url: config.urls.cluster - }).done(function () { - isClustered = true; - deferred.resolve(); - }).fail(function (xhr, status, error) { - if (xhr.status === 404) { - isClustered = false; - deferred.resolve(); - } else { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - } - }); - }).promise(); + return $.ajax({ + type: 'GET', + url: config.urls.flowConfig + }).done(function (response) { + isClustered = response.flowConfiguration.clustered; + }).fail(nf.Common.handleAjaxError); }; /** http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js index b8c1a58..8476621 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js @@ -51,7 +51,7 @@ nf.Summary = (function () { urls: { banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - cluster: '../nifi-api/controller/cluster' + flowConfig: '../nifi-api/flow/config' } }; @@ -61,26 +61,15 @@ nf.Summary = (function () { var initializeSummaryTable = function () { return $.Deferred(function (deferred) { $.ajax({ - type: 'HEAD', - url: config.urls.cluster - }).done(function () { - nf.SummaryTable.init(true).done(function () { + type: 'GET', + url: config.urls.flowConfig + }).done(function (response) { + nf.SummaryTable.init(response.flowConfiguration.clustered).done(function () { deferred.resolve(); }).fail(function () { deferred.reject(); }); - }).fail(function (xhr, status, error) { - if (xhr.status === 404) { - nf.SummaryTable.init(false).done(function () { - deferred.resolve(); - }).fail(function () { - deferred.reject(); - }); - } else { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - } - }); + }).fail(nf.Common.handleAjaxError); }).promise(); }; http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java index b8d6f35..adb335c 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/PersistentProvenanceRepository.java @@ -59,6 +59,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; @@ -68,6 +69,13 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.FSDirectory; +import org.apache.nifi.authorization.AccessDeniedException; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.events.EventReporter; import org.apache.nifi.processor.DataUnit; import org.apache.nifi.provenance.expiration.ExpirationAction; @@ -99,6 +107,7 @@ import org.apache.nifi.util.RingBuffer; import org.apache.nifi.util.RingBuffer.ForEachEvaluator; import org.apache.nifi.util.StopWatch; import org.apache.nifi.util.Tuple; +import org.apache.nifi.web.ResourceNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -163,7 +172,9 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository // we keep the last 1000 records on hand so that when the UI is opened and it asks for the last 1000 records we don't need to // read them. Since this is a very cheap operation to keep them, it's worth the tiny expense for the improved user experience. private final RingBuffer<ProvenanceEventRecord> latestRecords = new RingBuffer<>(1000); - private EventReporter eventReporter; + private EventReporter eventReporter; // effectively final + private Authorizer authorizer; // effectively final + private ProvenanceAuthorizableFactory resourceFactory; // effectively final public PersistentProvenanceRepository() throws IOException { this(createRepositoryConfiguration(), 10000); @@ -207,7 +218,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } @Override - public void initialize(final EventReporter eventReporter) throws IOException { + public void initialize(final EventReporter eventReporter, final Authorizer authorizer, final ProvenanceAuthorizableFactory resourceFactory) throws IOException { writeLock.lock(); try { if (initialized.getAndSet(true)) { @@ -215,6 +226,8 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } this.eventReporter = eventReporter; + this.authorizer = authorizer; + this.resourceFactory = resourceFactory; recover(); @@ -391,8 +404,46 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository persistRecord(events); } + public boolean isAuthorized(final ProvenanceEventRecord event, final NiFiUser user) { + if (authorizer == null || user == null) { + return true; + } + + final Authorizable eventAuthorizable; + try { + eventAuthorizable = resourceFactory.createProvenanceAuthorizable(event.getComponentId()); + } catch (final ResourceNotFoundException rnfe) { + return false; + } + + final AuthorizationResult result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, user); + return Result.Approved.equals(result.getResult()); + } + + protected void authorize(final ProvenanceEventRecord event, final NiFiUser user) { + if (authorizer == null) { + return; + } + + final Authorizable eventAuthorizable = resourceFactory.createProvenanceAuthorizable(event.getComponentId()); + eventAuthorizable.authorize(authorizer, RequestAction.READ, user); + } + + private List<ProvenanceEventRecord> filterUnauthorizedEvents(final List<ProvenanceEventRecord> events, final NiFiUser user) { + return events.stream().filter(event -> isAuthorized(event, user)).collect(Collectors.<ProvenanceEventRecord> toList()); + } + + private Set<ProvenanceEventRecord> replaceUnauthorizedWithPlaceholders(final Set<ProvenanceEventRecord> events, final NiFiUser user) { + return events.stream().map(event -> isAuthorized(event, user) ? event : new PlaceholderProvenanceEvent(event)).collect(Collectors.toSet()); + } + @Override public List<ProvenanceEventRecord> getEvents(final long firstRecordId, final int maxRecords) throws IOException { + return getEvents(firstRecordId, maxRecords, null); + } + + @Override + public List<ProvenanceEventRecord> getEvents(final long firstRecordId, final int maxRecords, final NiFiUser user) throws IOException { final List<ProvenanceEventRecord> records = new ArrayList<>(maxRecords); final List<Path> paths = getPathsForId(firstRecordId); @@ -417,7 +468,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository StandardProvenanceEventRecord record; while (records.size() < maxRecords && (record = reader.nextRecord()) != null) { - if (record.getEventId() >= firstRecordId) { + if (record.getEventId() >= firstRecordId && isAuthorized(record, user)) { records.add(record); } } @@ -1807,8 +1858,8 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository return new ArrayList<>(configuration.getSearchableAttributes()); } - QueryResult queryEvents(final Query query) throws IOException { - final QuerySubmission submission = submitQuery(query); + QueryResult queryEvents(final Query query, final NiFiUser user) throws IOException { + final QuerySubmission submission = submitQuery(query, user); final QueryResult result = submission.getResult(); while (!result.isFinished()) { try { @@ -1826,8 +1877,10 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } @Override - public QuerySubmission submitQuery(final Query query) { + public QuerySubmission submitQuery(final Query query, final NiFiUser user) { + final String userId = user.getIdentity(); final int numQueries = querySubmissionMap.size(); + if (numQueries > MAX_UNDELETED_QUERY_RESULTS) { throw new IllegalStateException("Cannot process query because there are currently " + numQueries + " queries whose results have not " + "been deleted due to poorly behaving clients not issuing DELETE requests. Please try again later."); @@ -1838,10 +1891,10 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } if (query.getSearchTerms().isEmpty() && query.getStartDate() == null && query.getEndDate() == null) { - final AsyncQuerySubmission result = new AsyncQuerySubmission(query, 1); + final AsyncQuerySubmission result = new AsyncQuerySubmission(query, 1, userId); if (latestRecords.getSize() >= query.getMaxResults()) { - final List<ProvenanceEventRecord> latestList = latestRecords.asList(); + final List<ProvenanceEventRecord> latestList = filterUnauthorizedEvents(latestRecords.asList(), user); final List<ProvenanceEventRecord> trimmed; if (latestList.size() > query.getMaxResults()) { trimmed = latestList.subList(latestList.size() - query.getMaxResults(), latestList.size()); @@ -1863,7 +1916,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository result.getResult().update(trimmed, totalNumDocs); } else { - queryExecService.submit(new GetMostRecentRunnable(query, result)); + queryExecService.submit(new GetMostRecentRunnable(query, result, user)); } querySubmissionMap.put(query.getIdentifier(), result); @@ -1874,14 +1927,14 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository final List<File> indexDirectories = indexConfig.getIndexDirectories( query.getStartDate() == null ? null : query.getStartDate().getTime(), query.getEndDate() == null ? null : query.getEndDate().getTime()); - final AsyncQuerySubmission result = new AsyncQuerySubmission(query, indexDirectories.size()); + final AsyncQuerySubmission result = new AsyncQuerySubmission(query, indexDirectories.size(), userId); querySubmissionMap.put(query.getIdentifier(), result); if (indexDirectories.isEmpty()) { result.getResult().update(Collections.<ProvenanceEventRecord>emptyList(), 0L); } else { for (final File indexDir : indexDirectories) { - queryExecService.submit(new QueryRunnable(query, result, indexDir, retrievalCount)); + queryExecService.submit(new QueryRunnable(query, result, user, indexDir, retrievalCount)); } } @@ -2054,13 +2107,13 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository }; } - Lineage computeLineage(final String flowFileUuid) throws IOException { - return computeLineage(Collections.<String>singleton(flowFileUuid), LineageComputationType.FLOWFILE_LINEAGE, null, 0L, Long.MAX_VALUE); + Lineage computeLineage(final String flowFileUuid, final NiFiUser user) throws IOException { + return computeLineage(Collections.<String> singleton(flowFileUuid), user, LineageComputationType.FLOWFILE_LINEAGE, null, 0L, Long.MAX_VALUE); } - private Lineage computeLineage(final Collection<String> flowFileUuids, final LineageComputationType computationType, final Long eventId, final Long startTimestamp, + private Lineage computeLineage(final Collection<String> flowFileUuids, final NiFiUser user, final LineageComputationType computationType, final Long eventId, final Long startTimestamp, final Long endTimestamp) throws IOException { - final AsyncLineageSubmission submission = submitLineageComputation(flowFileUuids, computationType, eventId, startTimestamp, endTimestamp); + final AsyncLineageSubmission submission = submitLineageComputation(flowFileUuids, user, computationType, eventId, startTimestamp, endTimestamp); final StandardLineageResult result = submission.getResult(); while (!result.isFinished()) { try { @@ -2077,29 +2130,31 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } @Override - public AsyncLineageSubmission submitLineageComputation(final String flowFileUuid) { - return submitLineageComputation(Collections.singleton(flowFileUuid), LineageComputationType.FLOWFILE_LINEAGE, null, 0L, Long.MAX_VALUE); + public AsyncLineageSubmission submitLineageComputation(final String flowFileUuid, final NiFiUser user) { + return submitLineageComputation(Collections.singleton(flowFileUuid), user, LineageComputationType.FLOWFILE_LINEAGE, null, 0L, Long.MAX_VALUE); } - private AsyncLineageSubmission submitLineageComputation(final Collection<String> flowFileUuids, final LineageComputationType computationType, + private AsyncLineageSubmission submitLineageComputation(final Collection<String> flowFileUuids, final NiFiUser user, final LineageComputationType computationType, final Long eventId, final long startTimestamp, final long endTimestamp) { final List<File> indexDirs = indexConfig.getIndexDirectories(startTimestamp, endTimestamp); - final AsyncLineageSubmission result = new AsyncLineageSubmission(computationType, eventId, flowFileUuids, indexDirs.size()); + final AsyncLineageSubmission result = new AsyncLineageSubmission(computationType, eventId, flowFileUuids, indexDirs.size(), user.getIdentity()); lineageSubmissionMap.put(result.getLineageIdentifier(), result); for (final File indexDir : indexDirs) { - queryExecService.submit(new ComputeLineageRunnable(flowFileUuids, result, indexDir)); + queryExecService.submit(new ComputeLineageRunnable(flowFileUuids, user, result, indexDir)); } return result; } @Override - public AsyncLineageSubmission submitExpandChildren(final long eventId) { + public AsyncLineageSubmission submitExpandChildren(final long eventId, final NiFiUser user) { + final String userId = user.getIdentity(); + try { final ProvenanceEventRecord event = getEvent(eventId); if (event == null) { - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); submission.getResult().update(Collections.<ProvenanceEventRecord>emptyList()); return submission; @@ -2110,15 +2165,15 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository case FORK: case JOIN: case REPLAY: - return submitLineageComputation(event.getChildUuids(), LineageComputationType.EXPAND_CHILDREN, eventId, event.getEventTime(), Long.MAX_VALUE); + return submitLineageComputation(event.getChildUuids(), user, LineageComputationType.EXPAND_CHILDREN, eventId, event.getEventTime(), Long.MAX_VALUE); default: - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); submission.getResult().setError("Event ID " + eventId + " indicates an event of type " + event.getEventType() + " so its children cannot be expanded"); return submission; } } catch (final IOException ioe) { - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); if (ioe.getMessage() == null) { @@ -2132,11 +2187,13 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } @Override - public AsyncLineageSubmission submitExpandParents(final long eventId) { + public AsyncLineageSubmission submitExpandParents(final long eventId, final NiFiUser user) { + final String userId = user.getIdentity(); + try { final ProvenanceEventRecord event = getEvent(eventId); if (event == null) { - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_CHILDREN, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); submission.getResult().update(Collections.<ProvenanceEventRecord>emptyList()); return submission; @@ -2147,16 +2204,16 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository case FORK: case CLONE: case REPLAY: - return submitLineageComputation(event.getParentUuids(), LineageComputationType.EXPAND_PARENTS, eventId, event.getLineageStartDate(), event.getEventTime()); + return submitLineageComputation(event.getParentUuids(), user, LineageComputationType.EXPAND_PARENTS, eventId, event.getLineageStartDate(), event.getEventTime()); default: { - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_PARENTS, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_PARENTS, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); submission.getResult().setError("Event ID " + eventId + " indicates an event of type " + event.getEventType() + " so its parents cannot be expanded"); return submission; } } } catch (final IOException ioe) { - final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_PARENTS, eventId, Collections.<String>emptyList(), 1); + final AsyncLineageSubmission submission = new AsyncLineageSubmission(LineageComputationType.EXPAND_PARENTS, eventId, Collections.<String> emptyList(), 1, userId); lineageSubmissionMap.put(submission.getLineageIdentifier(), submission); if (ioe.getMessage() == null) { @@ -2170,17 +2227,47 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } @Override - public AsyncLineageSubmission retrieveLineageSubmission(final String lineageIdentifier) { - return lineageSubmissionMap.get(lineageIdentifier); + public AsyncLineageSubmission retrieveLineageSubmission(final String lineageIdentifier, final NiFiUser user) { + final AsyncLineageSubmission submission = lineageSubmissionMap.get(lineageIdentifier); + final String userId = submission.getSubmitterIdentity(); + + if (user == null && userId == null) { + return submission; + } + + if (user == null) { + throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because no user id was provided"); + } + + if (userId == null || userId.equals(user.getIdentity())) { + return submission; + } + + throw new AccessDeniedException("Cannot retrieve Provenance Lineage Submission because " + user.getIdentity() + " is not the user who submitted the request"); } @Override - public QuerySubmission retrieveQuerySubmission(final String queryIdentifier) { - return querySubmissionMap.get(queryIdentifier); + public QuerySubmission retrieveQuerySubmission(final String queryIdentifier, final NiFiUser user) { + final QuerySubmission submission = querySubmissionMap.get(queryIdentifier); + + final String userId = submission.getSubmitterIdentity(); + + if (user == null && userId == null) { + return submission; + } + + if (user == null) { + throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided"); + } + + if (userId == null || userId.equals(user.getIdentity())) { + return submission; + } + + throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request"); } - @Override - public ProvenanceEventRecord getEvent(final long id) throws IOException { + private ProvenanceEventRecord getEvent(final long id) throws IOException { final List<ProvenanceEventRecord> records = getEvents(id, 1); if (records.isEmpty()) { return null; @@ -2192,6 +2279,17 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository return record; } + @Override + public ProvenanceEventRecord getEvent(final long id, final NiFiUser user) throws IOException { + final ProvenanceEventRecord event = getEvent(id); + if (event == null) { + return null; + } + + authorize(event, user); + return event; + } + private boolean needToRollover() { final long writtenSinceRollover = bytesWrittenSinceRollover.get(); @@ -2268,10 +2366,12 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository private final Query query; private final AsyncQuerySubmission submission; + private final NiFiUser user; - public GetMostRecentRunnable(final Query query, final AsyncQuerySubmission submission) { + public GetMostRecentRunnable(final Query query, final AsyncQuerySubmission submission, final NiFiUser user) { this.query = query; this.submission = submission; + this.user = user; } @Override @@ -2293,7 +2393,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository } final long totalNumDocs = maxEventId - minIndexedId; - final List<ProvenanceEventRecord> mostRecent = getEvents(startIndex, maxResults); + final List<ProvenanceEventRecord> mostRecent = getEvents(startIndex, maxResults, user); submission.getResult().update(mostRecent, totalNumDocs); } catch (final IOException ioe) { logger.error("Failed to retrieve records from Provenance Repository: " + ioe.toString()); @@ -2314,12 +2414,14 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository private final Query query; private final AsyncQuerySubmission submission; + private final NiFiUser user; private final File indexDir; private final AtomicInteger retrievalCount; - public QueryRunnable(final Query query, final AsyncQuerySubmission submission, final File indexDir, final AtomicInteger retrievalCount) { + public QueryRunnable(final Query query, final AsyncQuerySubmission submission, final NiFiUser user, final File indexDir, final AtomicInteger retrievalCount) { this.query = query; this.submission = submission; + this.user = user; this.indexDir = indexDir; this.retrievalCount = retrievalCount; } @@ -2328,7 +2430,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository public void run() { try { final IndexSearch search = new IndexSearch(PersistentProvenanceRepository.this, indexDir, indexManager, maxAttributeChars); - final StandardQueryResult queryResult = search.search(query, retrievalCount, firstEventTimestamp); + final StandardQueryResult queryResult = search.search(query, user, retrievalCount, firstEventTimestamp); submission.getResult().update(queryResult.getMatchingEvents(), queryResult.getTotalHitCount()); } catch (final Throwable t) { logger.error("Failed to query Provenance Repository Index {} due to {}", indexDir, t.toString()); @@ -2348,11 +2450,13 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository private class ComputeLineageRunnable implements Runnable { private final Collection<String> flowFileUuids; + private final NiFiUser user; private final File indexDir; private final AsyncLineageSubmission submission; - public ComputeLineageRunnable(final Collection<String> flowFileUuids, final AsyncLineageSubmission submission, final File indexDir) { + public ComputeLineageRunnable(final Collection<String> flowFileUuids, final NiFiUser user, final AsyncLineageSubmission submission, final File indexDir) { this.flowFileUuids = flowFileUuids; + this.user = user; this.submission = submission; this.indexDir = indexDir; } @@ -2368,7 +2472,7 @@ public class PersistentProvenanceRepository implements ProvenanceEventRepository indexManager, indexDir, null, flowFileUuids, maxAttributeChars); final StandardLineageResult result = submission.getResult(); - result.update(matchingRecords); + result.update(replaceUnauthorizedWithPlaceholders(matchingRecords, user)); logger.info("Successfully created Lineage for FlowFiles with UUIDs {} in {} milliseconds; Lineage contains {} nodes and {} edges", flowFileUuids, result.getComputationTime(TimeUnit.MILLISECONDS), result.getNodes().size(), result.getEdges().size()); http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/authorization/AuthorizationCheck.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/authorization/AuthorizationCheck.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/authorization/AuthorizationCheck.java new file mode 100644 index 0000000..3627572 --- /dev/null +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/authorization/AuthorizationCheck.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nifi.provenance.authorization; + +import org.apache.nifi.provenance.ProvenanceEventRecord; + +public interface AuthorizationCheck { + boolean isAuthorized(ProvenanceEventRecord event); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/DocsReader.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/DocsReader.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/DocsReader.java index b195559..e448f27 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/DocsReader.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/DocsReader.java @@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.nifi.provenance.ProvenanceEventRecord; import org.apache.nifi.provenance.SearchableFields; import org.apache.nifi.provenance.StandardProvenanceEventRecord; +import org.apache.nifi.provenance.authorization.AuthorizationCheck; import org.apache.nifi.provenance.serialization.RecordReader; import org.apache.nifi.provenance.serialization.RecordReaders; import org.apache.nifi.provenance.toc.TocReader; @@ -47,7 +48,7 @@ import org.slf4j.LoggerFactory; class DocsReader { private final Logger logger = LoggerFactory.getLogger(DocsReader.class); - public Set<ProvenanceEventRecord> read(final TopDocs topDocs, final IndexReader indexReader, final Collection<Path> allProvenanceLogFiles, + public Set<ProvenanceEventRecord> read(final TopDocs topDocs, final AuthorizationCheck authCheck, final IndexReader indexReader, final Collection<Path> allProvenanceLogFiles, final AtomicInteger retrievalCount, final int maxResults, final int maxAttributeChars) throws IOException { if (retrievalCount.get() >= maxResults) { return Collections.emptySet(); @@ -65,7 +66,7 @@ class DocsReader { final long readDocuments = System.nanoTime() - start; logger.debug("Reading {} Lucene Documents took {} millis", docs.size(), TimeUnit.NANOSECONDS.toMillis(readDocuments)); - return read(docs, allProvenanceLogFiles, retrievalCount, maxResults, maxAttributeChars); + return read(docs, authCheck, allProvenanceLogFiles, retrievalCount, maxResults, maxAttributeChars); } @@ -104,7 +105,7 @@ class DocsReader { return record; } - public Set<ProvenanceEventRecord> read(final List<Document> docs, final Collection<Path> allProvenanceLogFiles, + public Set<ProvenanceEventRecord> read(final List<Document> docs, final AuthorizationCheck authCheck, final Collection<Path> allProvenanceLogFiles, final AtomicInteger retrievalCount, final int maxResults, final int maxAttributeChars) throws IOException { if (retrievalCount.get() >= maxResults) { @@ -128,9 +129,9 @@ class DocsReader { Iterator<Document> docIter = byStorageNameDocGroups.get(storageFileName).iterator(); while (docIter.hasNext() && retrievalCount.getAndIncrement() < maxResults) { - ProvenanceEventRecord eRec = this.getRecord(docIter.next(), reader); - if (eRec != null) { - matchingRecords.add(eRec); + ProvenanceEventRecord event = this.getRecord(docIter.next(), reader); + if (event != null && authCheck.isAuthorized(event)) { + matchingRecords.add(event); eventsReadThisFile++; } } http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/IndexSearch.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/IndexSearch.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/IndexSearch.java index 65a06ac..00e5f38 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/IndexSearch.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/IndexSearch.java @@ -28,9 +28,11 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.TopDocs; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.provenance.PersistentProvenanceRepository; import org.apache.nifi.provenance.ProvenanceEventRecord; import org.apache.nifi.provenance.StandardQueryResult; +import org.apache.nifi.provenance.authorization.AuthorizationCheck; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +50,8 @@ public class IndexSearch { this.maxAttributeChars = maxAttributeChars; } - public StandardQueryResult search(final org.apache.nifi.provenance.search.Query provenanceQuery, final AtomicInteger retrievedCount, final long firstEventTimestamp) throws IOException { + public StandardQueryResult search(final org.apache.nifi.provenance.search.Query provenanceQuery, final NiFiUser user, final AtomicInteger retrievedCount, + final long firstEventTimestamp) throws IOException { if (retrievedCount.get() >= provenanceQuery.getMaxResults()) { final StandardQueryResult sqr = new StandardQueryResult(provenanceQuery, 1); sqr.update(Collections.<ProvenanceEventRecord> emptyList(), 0L); @@ -102,7 +105,10 @@ public class IndexSearch { } final DocsReader docsReader = new DocsReader(); - matchingRecords = docsReader.read(topDocs, searcher.getIndexReader(), repository.getAllLogFiles(), retrievedCount, + + final AuthorizationCheck authCheck = event -> repository.isAuthorized(event, user); + + matchingRecords = docsReader.read(topDocs, authCheck, searcher.getIndexReader(), repository.getAllLogFiles(), retrievedCount, provenanceQuery.getMaxResults(), maxAttributeChars); final long readRecordsNanos = System.nanoTime() - finishSearch; http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/LineageQuery.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/LineageQuery.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/LineageQuery.java index ce60e03..3a2d6e1 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/LineageQuery.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/lucene/LineageQuery.java @@ -36,6 +36,7 @@ import org.apache.lucene.search.TopDocs; import org.apache.nifi.provenance.PersistentProvenanceRepository; import org.apache.nifi.provenance.ProvenanceEventRecord; import org.apache.nifi.provenance.SearchableFields; +import org.apache.nifi.provenance.authorization.AuthorizationCheck; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,8 +94,12 @@ public class LineageQuery { final TopDocs uuidQueryTopDocs = searcher.search(query, MAX_QUERY_RESULTS); final long searchEnd = System.nanoTime(); + // Always authorized. We do this because we need to pull back the event, regardless of whether or not + // the user is truly authorized, because instead of ignoring unauthorized events, we want to replace them. + final AuthorizationCheck authCheck = event -> true; + final DocsReader docsReader = new DocsReader(); - final Set<ProvenanceEventRecord> recs = docsReader.read(uuidQueryTopDocs, searcher.getIndexReader(), repo.getAllLogFiles(), + final Set<ProvenanceEventRecord> recs = docsReader.read(uuidQueryTopDocs, authCheck, searcher.getIndexReader(), repo.getAllLogFiles(), new AtomicInteger(0), Integer.MAX_VALUE, maxAttributeChars); final long readDocsEnd = System.nanoTime();
