From e8a5698c3fcb0ccd9f270ea57b6c0c3c9714d910 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Fri, 1 Jul 2016 15:28:36 -0400 Subject: [PATCH] NIFI-1781: - Updating UI according to permissions through out the application. - Shuffling provenance events, template, and cluster search REST APIs according to resources being authorized. - Moving template upload controls. - Removing username where appropriate. - Addressing issues when authorizing flow configuration actions. - Updating provenance event authorizations to consider connections. - Code clean up. --- .../java/org/apache/nifi/action/Action.java | 2 - .../authorization/AuthorizationRequest.java | 38 +- .../apache/nifi/authorization/Authorizer.java | 2 +- .../authorization/resource/Authorizable.java | 48 ++- .../nifi/authorization/user/NiFiUser.java | 5 - .../nifi/web/NiFiWebConfigurationContext.java | 11 +- .../org/apache/nifi/util/NiFiProperties.java | 3 +- .../org/apache/nifi/web/util/WebUtils.java | 94 +---- .../admin/AuditDataSourceFactoryBean.java | 31 +- .../admin/dao/impl/StandardActionDAO.java | 64 ++- .../nifi/admin/dao/impl/StandardKeyDAO.java | 7 +- .../org/apache/nifi/history/HistoryQuery.java | 10 +- .../apache/nifi/history/PreviousValue.java | 10 +- .../nifi/web/api/dto/AccessStatusDTO.java | 15 - .../nifi/web/api/dto/PreviousValueDTO.java | 15 +- .../nifi/web/api/dto/action/ActionDTO.java | 22 +- .../web/api/dto/action/HistoryQueryDTO.java | 19 +- .../dto/search/UserGroupSearchResultDTO.java | 44 -- .../api/dto/search/UserSearchResultDTO.java | 59 --- .../nifi/web/api/entity/AuthorityEntity.java | 54 --- ...tityEntity.java => CurrentUserEntity.java} | 65 ++- .../nifi/web/api/entity/TemplateEntity.java | 5 +- .../nifi/web/api/entity/TemplatesEntity.java | 14 +- .../api/entity/UserSearchResultsEntity.java | 56 --- .../nifi/authorization/FileAuthorizer.java | 2 +- .../authorization/user/NiFiUserUtils.java | 4 +- .../authorization/user/StandardNiFiUser.java | 19 +- .../org/apache/nifi/controller/Template.java | 53 ++- .../nifi/controller/FlowController.java | 80 ++-- .../nifi/groups/StandardProcessGroup.java | 4 + .../apache/nifi/action/FlowChangeAction.java | 10 - .../nifi/audit/ComponentStateAuditor.java | 3 - .../apache/nifi/audit/ControllerAuditor.java | 2 - .../nifi/audit/ControllerServiceAuditor.java | 22 +- .../org/apache/nifi/audit/FunnelAuditor.java | 8 +- .../org/apache/nifi/audit/PortAuditor.java | 3 - .../nifi/audit/ProcessGroupAuditor.java | 3 - .../apache/nifi/audit/ProcessorAuditor.java | 29 +- .../nifi/audit/RelationshipAuditor.java | 25 +- .../nifi/audit/RemoteProcessGroupAuditor.java | 3 - .../nifi/audit/ReportingTaskAuditor.java | 19 +- .../org/apache/nifi/audit/SnippetAuditor.java | 1 - .../apache/nifi/web/AuthorizableLookup.java | 14 + .../apache/nifi/web/NiFiServiceFacade.java | 11 +- .../web/NiFiWebApiSecurityConfiguration.java | 17 - .../nifi/web/StandardAuthorizableLookup.java | 36 ++ .../nifi/web/StandardNiFiContentAccess.java | 46 +-- .../nifi/web/StandardNiFiServiceFacade.java | 75 +++- .../StandardNiFiWebConfigurationContext.java | 30 +- .../apache/nifi/web/api/AccessResource.java | 41 +- .../nifi/web/api/ApplicationResource.java | 27 -- .../nifi/web/api/ControllerResource.java | 80 ---- .../org/apache/nifi/web/api/FlowResource.java | 289 ++++++++----- .../nifi/web/api/ProcessGroupResource.java | 130 ++---- .../nifi/web/api/ProvenanceEventResource.java | 391 ++++++++++++++++++ .../nifi/web/api/ProvenanceResource.java | 366 +--------------- .../apache/nifi/web/api/TemplateResource.java | 60 ++- .../apache/nifi/web/api/dto/DtoFactory.java | 11 +- .../nifi/web/controller/ControllerFacade.java | 212 ++++++++-- .../web/dao/impl/StandardConnectionDAO.java | 2 +- .../main/resources/nifi-web-api-context.xml | 6 + .../web/revision/NaiveRevisionManager.java | 4 +- .../node/NodeAuthorizedUserFilter.java | 128 ------ .../x509/X509AuthenticationProvider.java | 2 +- .../src/main/webapp/WEB-INF/pages/canvas.jsp | 2 + .../main/webapp/WEB-INF/pages/templates.jsp | 1 - .../WEB-INF/partials/canvas/canvas-header.jsp | 21 +- .../WEB-INF/partials/canvas/navigation.jsp | 8 +- .../canvas/upload-template-dialog.jsp | 35 ++ .../partials/templates/templates-content.jsp | 19 - .../src/main/webapp/css/header.css | 11 +- .../nifi-web-ui/src/main/webapp/css/main.css | 75 ++++ .../src/main/webapp/css/templates.css | 90 ---- .../nf-ng-canvas-global-menu-controller.js | 50 +-- .../nf-ng-canvas-operate-controller.js | 138 +++++++ .../nf-ng-canvas-toolbox-controller.js | 27 +- .../components/nf-ng-template-component.js | 16 +- .../main/webapp/js/nf/canvas/nf-actions.js | 7 + .../src/main/webapp/js/nf/canvas/nf-canvas.js | 92 +---- .../webapp/js/nf/canvas/nf-context-menu.js | 2 +- .../nf/canvas/nf-processor-configuration.js | 5 - .../canvas/nf-remote-process-group-ports.js | 19 +- .../main/webapp/js/nf/canvas/nf-settings.js | 32 +- .../webapp/js/nf/cluster/nf-cluster-table.js | 6 +- .../main/webapp/js/nf/cluster/nf-cluster.js | 35 +- .../js/nf/counters/nf-counters-table.js | 2 +- .../main/webapp/js/nf/counters/nf-counters.js | 36 +- .../webapp/js/nf/history/nf-history-model.js | 2 +- .../webapp/js/nf/history/nf-history-table.js | 19 +- .../main/webapp/js/nf/history/nf-history.js | 36 +- .../src/main/webapp/js/nf/login/nf-login.js | 2 - .../src/main/webapp/js/nf/nf-common.js | 117 ++++-- .../js/nf/provenance/nf-provenance-lineage.js | 2 +- .../js/nf/provenance/nf-provenance-table.js | 95 ++--- .../webapp/js/nf/provenance/nf-provenance.js | 35 +- .../webapp/js/nf/summary/nf-cluster-search.js | 2 +- .../js/nf/templates/nf-templates-table.js | 100 +++-- .../webapp/js/nf/templates/nf-templates.js | 124 +----- .../PersistentProvenanceRepository.java | 94 ++--- .../TestPersistentProvenanceRepository.java | 59 ++- .../VolatileProvenanceRepository.java | 46 +-- .../TestVolatileProvenanceRepository.java | 24 +- .../script/ExecuteScriptGroovyTest.groovy | 4 +- 103 files changed, 2097 insertions(+), 2354 deletions(-) delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserGroupSearchResultDTO.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserSearchResultDTO.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AuthorityEntity.java rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/{IdentityEntity.java => CurrentUserEntity.java} (50%) delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UserSearchResultsEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceEventResource.java delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/upload-template-dialog.jsp diff --git a/nifi-api/src/main/java/org/apache/nifi/action/Action.java b/nifi-api/src/main/java/org/apache/nifi/action/Action.java index 4ab167e26880..ed6505fcd9d0 100644 --- a/nifi-api/src/main/java/org/apache/nifi/action/Action.java +++ b/nifi-api/src/main/java/org/apache/nifi/action/Action.java @@ -33,8 +33,6 @@ public interface Action extends Serializable { String getUserIdentity(); - String getUserName(); - String getSourceId(); String getSourceName(); diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java index 7e6999c09a60..da0a276b1eae 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java @@ -22,7 +22,7 @@ import java.util.Objects; /** - * Represents an authorization request for a given user/entity performing an action against a resource within some context. + * Represents an authorization request for a given user/entity performing an action against a resource within some userContext. */ public class AuthorizationRequest { @@ -31,8 +31,8 @@ public class AuthorizationRequest { private final RequestAction action; private final boolean isAccessAttempt; private final boolean isAnonymous; - private final Map context; - private final Map eventAttributes; + private final Map userContext; + private final Map resourceContext; private AuthorizationRequest(final Builder builder) { Objects.requireNonNull(builder.resource, "The resource is required when creating an authorization request"); @@ -45,8 +45,8 @@ private AuthorizationRequest(final Builder builder) { this.action = builder.action; this.isAccessAttempt = builder.isAccessAttempt; this.isAnonymous = builder.isAnonymous; - this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context); - this.eventAttributes = builder.context == null ? null : Collections.unmodifiableMap(builder.eventAttributes); + this.userContext = builder.userContext == null ? null : Collections.unmodifiableMap(builder.userContext); + this.resourceContext = builder.resourceContext == null ? null : Collections.unmodifiableMap(builder.resourceContext); } /** @@ -95,12 +95,12 @@ public RequestAction getAction() { } /** - * The context of the user request to make additional access decisions. May be null. + * The userContext of the user request to make additional access decisions. May be null. * - * @return The context of the user request + * @return The userContext of the user request */ - public Map getContext() { - return context; + public Map getUserContext() { + return userContext; } /** @@ -108,8 +108,8 @@ public Map getContext() { * * @return The event attributes */ - public Map getEventAttributes() { - return eventAttributes; + public Map getResourceContext() { + return resourceContext; } /** @@ -122,8 +122,8 @@ public static final class Builder { private Boolean isAnonymous; private Boolean isAccessAttempt; private RequestAction action; - private Map context; - private Map eventAttributes; + private Map userContext; + private Map resourceContext; public Builder resource(final Resource resource) { this.resource = resource; @@ -150,13 +150,17 @@ public Builder action(final RequestAction action) { return this; } - public Builder context(final Map context) { - this.context = new HashMap<>(context); + public Builder userContext(final Map userContext) { + if (userContext != null) { + this.userContext = new HashMap<>(userContext); + } return this; } - public Builder eventAttributes(final Map eventAttributes) { - this.eventAttributes = new HashMap<>(eventAttributes); + public Builder resourceContext(final Map resourceContext) { + if (resourceContext != null) { + this.resourceContext = new HashMap<>(resourceContext); + } return this; } diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/Authorizer.java b/nifi-api/src/main/java/org/apache/nifi/authorization/Authorizer.java index 5aec6f0b0b68..cb8c7f1dc379 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/Authorizer.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/Authorizer.java @@ -30,7 +30,7 @@ public interface Authorizer { * * @param request The authorization request * @return the authorization result - * @throws AuthorizationAccessException if unable to access the authorities + * @throws AuthorizationAccessException if unable to access the policies */ AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException; diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java b/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java index 09fab19b5722..90374dd97301 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java @@ -25,6 +25,8 @@ import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.user.NiFiUser; +import java.util.Map; + public interface Authorizable { /** @@ -64,7 +66,7 @@ default boolean isAuthorized(Authorizer authorizer, RequestAction action, NiFiUs * @param user user * @return is authorized */ - default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user) { + default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user, Map resourceContext) { // TODO - include user details context // build the request @@ -74,6 +76,7 @@ default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAct .accessAttempt(false) .action(action) .resource(getResource()) + .resourceContext(resourceContext) .build(); // perform the authorization @@ -92,23 +95,40 @@ default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAct } } + /** + * Returns the result of an authorization request for the specified user for the specified action on the specified + * resource. This method does not imply the user is directly attempting to access the specified resource. If the user is + * attempting a direct access use Authorizable.authorize(). + * + * @param authorizer authorizer + * @param action action + * @param user user + * @return is authorized + */ + default AuthorizationResult checkAuthorization(Authorizer authorizer, RequestAction action, NiFiUser user) { + return checkAuthorization(authorizer, action, user, null); + } + /** * Authorizes the current user for the specified action on the specified resource. This method does imply the user is * directly accessing the specified resource. * * @param authorizer authorizer * @param action action + * @param user user + * @param resourceContext resource context */ - default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user) throws AccessDeniedException { + default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map resourceContext) throws AccessDeniedException { // TODO - include user details context final AuthorizationRequest request = new AuthorizationRequest.Builder() - .identity(user.getIdentity()) - .anonymous(user.isAnonymous()) - .accessAttempt(true) - .action(action) - .resource(getResource()) - .build(); + .identity(user.getIdentity()) + .anonymous(user.isAnonymous()) + .accessAttempt(true) + .action(action) + .resource(getResource()) + .resourceContext(resourceContext) + .build(); final AuthorizationResult result = authorizer.authorize(request); if (Result.ResourceNotFound.equals(result.getResult())) { @@ -122,4 +142,16 @@ default void authorize(Authorizer authorizer, RequestAction action, NiFiUser use throw new AccessDeniedException(result.getExplanation()); } } + + /** + * Authorizes the current user for the specified action on the specified resource. This method does imply the user is + * directly accessing the specified resource. + * + * @param authorizer authorizer + * @param action action + * @param user user + */ + default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user) throws AccessDeniedException { + authorize(authorizer, action, user, null); + } } diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java b/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java index d5dee54b1466..9ec04e2ca0d7 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java @@ -27,11 +27,6 @@ public interface NiFiUser { */ String getIdentity(); - /** - * @return the user name for this user - */ - String getUserName(); - /** * @return the next user in the proxied entities chain, or null if no more users exist in the chain. */ diff --git a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java index a0a605cadcfb..5082af2cd29e 100644 --- a/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java +++ b/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java @@ -16,11 +16,11 @@ */ package org.apache.nifi.web; +import org.apache.nifi.controller.ControllerService; + import java.util.Collection; import java.util.Map; -import org.apache.nifi.controller.ControllerService; - /** * NiFi web context providing limited access to dataflow configuration for * component custom UIs. @@ -58,12 +58,7 @@ public interface NiFiWebConfigurationContext { /** * @return the current user identity. The value may be a DN, an email, a username, or any string that identities the user. Returns null if no user is found */ - String getCurrentUserDn(); - - /** - * @return the current user name. Returns null if no user is found - */ - String getCurrentUserName(); + String getCurrentUserIdentity(); /** * Sets the annotation data for the underlying component. diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java index 73a54c59e145..2e84c5b4b6eb 100644 --- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java +++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java @@ -518,7 +518,7 @@ public File getAuthorizerConfigurationFile() { } /** - * @return the user authorities file + * @return the user login identity provider file */ public File getLoginIdentityProviderConfigurationFile() { final String value = getProperty(LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE); @@ -774,7 +774,6 @@ public boolean isKerberosServiceSupportEnabled() { * Returns true if client certificates are required for REST API. Determined if the following conditions are all true: * * - login identity provider is not populated - * - anonymous authorities is empty * - Kerberos service support is not enabled * * @return true if client certificates are required for access to the REST API diff --git a/nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/util/WebUtils.java b/nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/util/WebUtils.java index e27f91ccdc1c..18083ca77f95 100644 --- a/nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/util/WebUtils.java +++ b/nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/util/WebUtils.java @@ -16,12 +16,20 @@ */ package org.apache.nifi.web.util; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.json.JSONConfiguration; +import com.sun.jersey.client.urlconnection.HTTPSProperties; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.security.util.CertificateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; @@ -29,25 +37,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; - -import org.apache.nifi.security.util.CertificateUtils; - -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.config.ClientConfig; -import com.sun.jersey.api.client.config.DefaultClientConfig; -import com.sun.jersey.api.json.JSONConfiguration; -import com.sun.jersey.client.urlconnection.HTTPSProperties; - /** * Common utilities related to web development. * @@ -139,59 +128,4 @@ public boolean verify(final String hostname, final SSLSession ssls) { } - /** - * Serializes the given object to hexadecimal. Serialization uses Java's - * native serialization mechanism, the ObjectOutputStream. - * - * @param obj an object - * @return the serialized object as hex - */ - public static String serializeObjectToHex(final Serializable obj) { - - final ByteArrayOutputStream serializedObj = new ByteArrayOutputStream(); - - // IOException can never be thrown because we are serializing to an in memory byte array - try { - final ObjectOutputStream oos = new ObjectOutputStream(serializedObj); - oos.writeObject(obj); - oos.close(); - } catch (final IOException ioe) { - throw new RuntimeException(ioe); - } - - logger.debug(String.format("Serialized object '%s' size: %d", obj, serializedObj.size())); - - // hex encode the binary - return new String(Hex.encodeHex(serializedObj.toByteArray(), /* tolowercase */ true)); - } - - /** - * Deserializes a Java serialized, hex-encoded string into a Java object. - * This method is the inverse of the serializeObjectToHex method in this - * class. - * - * @param hexEncodedObject a string - * @return the object - * @throws ClassNotFoundException if the class could not be found - */ - public static Serializable deserializeHexToObject(final String hexEncodedObject) throws ClassNotFoundException { - - // decode the hex encoded object - byte[] serializedObj; - try { - serializedObj = Hex.decodeHex(hexEncodedObject.toCharArray()); - } catch (final DecoderException de) { - throw new IllegalArgumentException(de); - } - - // IOException can never be thrown because we are deserializing from an in memory byte array - try { - // deserialize bytes into object - ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedObj)); - return (Serializable) ois.readObject(); - } catch (final IOException ioe) { - throw new RuntimeException(ioe); - } - - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java index 87cd420fd387..18df4bc57211 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/AuditDataSourceFactoryBean.java @@ -16,18 +16,19 @@ */ package org.apache.nifi.admin; -import java.io.File; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; import org.apache.commons.lang3.StringUtils; -import org.h2.jdbcx.JdbcConnectionPool; import org.apache.nifi.util.NiFiProperties; +import org.h2.jdbcx.JdbcConnectionPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.FactoryBean; +import java.io.File; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + /** * */ @@ -38,7 +39,7 @@ public class AuditDataSourceFactoryBean implements FactoryBean { private static final int MAX_CONNECTIONS = 5; // database file name - private static final String AUDIT_DATABASE_FILE_NAME = "nifi-audit"; + private static final String AUDIT_DATABASE_FILE_NAME = "nifi-flow-audit"; // ------------ // action table @@ -46,7 +47,6 @@ public class AuditDataSourceFactoryBean implements FactoryBean { private static final String CREATE_ACTION_TABLE = "CREATE TABLE ACTION (" + "ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT, " + "IDENTITY VARCHAR2(4096) NOT NULL, " - + "USER_NAME VARCHAR2(4096) NOT NULL, " + "SOURCE_ID VARCHAR2(100) NOT NULL, " + "SOURCE_NAME VARCHAR2(1000) NOT NULL, " + "SOURCE_TYPE VARCHAR2(1000) NOT NULL, " @@ -107,10 +107,6 @@ public class AuditDataSourceFactoryBean implements FactoryBean { + "FOREIGN KEY (ACTION_ID) REFERENCES ACTION(ID)" + ")"; - private static final String RENAME_DN_COLUMN = "ALTER TABLE ACTION ALTER COLUMN USER_DN RENAME TO IDENTITY"; - private static final String RESIZE_IDENTITY_COLUMN = "ALTER TABLE ACTION MODIFY IDENTITY VARCHAR(4096)"; - private static final String RESIZE_USER_NAME_COLUMN = "ALTER TABLE ACTION MODIFY USER_NAME VARCHAR(4096)"; - private JdbcConnectionPool connectionPool; private NiFiProperties properties; @@ -173,17 +169,6 @@ public Object getObject() throws Exception { statement.execute(CREATE_CONFIGURE_DETAILS_TABLE); statement.execute(CREATE_CONNECT_DETAILS_TABLE); statement.execute(CREATE_PURGE_DETAILS_TABLE); - } else { - logger.info("Existing database found and connected to at: " + databaseUrl); - RepositoryUtils.closeQuietly(rs); - - // check if the DN column exists to see if we need to transform the table - rs = connection.getMetaData().getColumns(null, null, "ACTION", "USER_DN"); - if (rs.next()) { - statement.execute(RENAME_DN_COLUMN); - statement.execute(RESIZE_IDENTITY_COLUMN); - statement.execute(RESIZE_USER_NAME_COLUMN); - } } // commit any changes diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java index 8fdfd3494fb8..df1774c5be4f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java @@ -16,18 +16,7 @@ */ package org.apache.nifi.admin.dao.impl; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -52,7 +41,19 @@ import org.apache.nifi.history.History; import org.apache.nifi.history.HistoryQuery; import org.apache.nifi.history.PreviousValue; -import org.apache.commons.lang3.StringUtils; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * @@ -63,15 +64,14 @@ public class StandardActionDAO implements ActionDAO { // action table // ------------ private static final String INSERT_ACTION = "INSERT INTO ACTION (" - + "IDENTITY, USER_NAME, SOURCE_ID, SOURCE_NAME, SOURCE_TYPE, OPERATION, ACTION_TIMESTAMP" + + "IDENTITY, SOURCE_ID, SOURCE_NAME, SOURCE_TYPE, OPERATION, ACTION_TIMESTAMP" + ") VALUES (" + "?, " + "?, " + "?, " + "?, " + "?, " - + "?, " - + "?, " + + "? " + ")"; // ----------------- @@ -178,7 +178,7 @@ public class StandardActionDAO implements ActionDAO { private static final String SELECT_PREVIOUS_VALUES = "SELECT CD.VALUE, " + "A.ACTION_TIMESTAMP, " - + "A.USER_NAME " + + "A.IDENTITY " + "FROM CONFIGURE_DETAILS CD " + "INNER JOIN ACTION A " + "ON CD.ACTION_ID = A.ID " @@ -198,7 +198,7 @@ public StandardActionDAO(Connection connection) { this.columnMap.put("sourceName", "SOURCE_NAME"); this.columnMap.put("sourceType", "SOURCE_TYPE"); this.columnMap.put("operation", "OPERATION"); - this.columnMap.put("userName", "USER_NAME"); + this.columnMap.put("userIdentity", "IDENTITY"); } @Override @@ -217,19 +217,17 @@ public Action createAction(Action action) throws DataAccessException { // obtain a statement to insert to the action table statement = connection.prepareStatement(INSERT_ACTION, Statement.RETURN_GENERATED_KEYS); statement.setString(1, StringUtils.left(action.getUserIdentity(), 4096)); - statement.setString(2, StringUtils.left(action.getUserName(), 4096)); - statement.setString(3, action.getSourceId()); - statement.setString(4, StringUtils.left(action.getSourceName(), 1000)); - statement.setString(5, action.getSourceType().toString()); - statement.setString(6, action.getOperation().toString()); - statement.setTimestamp(7, new java.sql.Timestamp(action.getTimestamp().getTime())); + statement.setString(2, action.getSourceId()); + statement.setString(3, StringUtils.left(action.getSourceName(), 1000)); + statement.setString(4, action.getSourceType().toString()); + statement.setString(5, action.getOperation().toString()); + statement.setTimestamp(6, new java.sql.Timestamp(action.getTimestamp().getTime())); // insert the action int updateCount = statement.executeUpdate(); final FlowChangeAction createdAction = new FlowChangeAction(); createdAction.setUserIdentity(action.getUserIdentity()); - createdAction.setUserName(action.getUserName()); createdAction.setSourceId(action.getSourceId()); createdAction.setSourceName(action.getSourceName()); createdAction.setSourceType(action.getSourceType()); @@ -458,8 +456,8 @@ public History findActions(HistoryQuery historyQuery) throws DataAccessException } // append the user id as necessary - if (historyQuery.getUserName() != null) { - where.add("UPPER(USER_NAME) LIKE ?"); + if (historyQuery.getUserIdentity() != null) { + where.add("UPPER(IDENTITY) LIKE ?"); } // append the source id as necessary @@ -487,8 +485,8 @@ public History findActions(HistoryQuery historyQuery) throws DataAccessException } // set the user id as necessary - if (historyQuery.getUserName() != null) { - statement.setString(paramIndex++, "%" + historyQuery.getUserName().toUpperCase() + "%"); + if (historyQuery.getUserIdentity() != null) { + statement.setString(paramIndex++, "%" + historyQuery.getUserIdentity().toUpperCase() + "%"); } // set the source id as necessary @@ -535,8 +533,8 @@ public History findActions(HistoryQuery historyQuery) throws DataAccessException } // set the user id as necessary - if (historyQuery.getUserName() != null) { - statement.setString(paramIndex++, "%" + historyQuery.getUserName().toUpperCase() + "%"); + if (historyQuery.getUserIdentity() != null) { + statement.setString(paramIndex++, "%" + historyQuery.getUserIdentity().toUpperCase() + "%"); } // set the source id as necessary @@ -562,7 +560,6 @@ public History findActions(HistoryQuery historyQuery) throws DataAccessException FlowChangeAction action = new FlowChangeAction(); action.setId(actionId); action.setUserIdentity(rs.getString("IDENTITY")); - action.setUserName(rs.getString("USER_NAME")); action.setOperation(Operation.valueOf(rs.getString("OPERATION"))); action.setTimestamp(new Date(rs.getTimestamp("ACTION_TIMESTAMP").getTime())); action.setSourceId(rs.getString("SOURCE_ID")); @@ -636,7 +633,6 @@ public Action getAction(Integer actionId) throws DataAccessException { action = new FlowChangeAction(); action.setId(rs.getInt("ID")); action.setUserIdentity(rs.getString("IDENTITY")); - action.setUserName(rs.getString("USER_NAME")); action.setOperation(operation); action.setTimestamp(new Date(rs.getTimestamp("ACTION_TIMESTAMP").getTime())); action.setSourceId(rs.getString("SOURCE_ID")); @@ -907,7 +903,7 @@ private List getPreviousValuesForProperty(final String componentI final PreviousValue previousValue = new PreviousValue(); previousValue.setPreviousValue(rs.getString("VALUE")); previousValue.setTimestamp(new Date(rs.getTimestamp("ACTION_TIMESTAMP").getTime())); - previousValue.setUserName(rs.getString("USER_NAME")); + previousValue.setUserIdentity(rs.getString("IDENTITY")); previousValues.add(previousValue); } } catch (SQLException sqle) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java index cc337fdafcea..9d1936126445 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardKeyDAO.java @@ -157,16 +157,11 @@ public Key createKey(final String identity) { @Override public void deleteKeys(String identity) { - // ensure there are some authorities to create PreparedStatement statement = null; try { // add each authority for the specified user statement = connection.prepareStatement(DELETE_KEYS); - statement.setString(1, identity); - - // insert the authorities - int count = statement.executeUpdate(); - System.out.println(); + statement.executeUpdate(); } catch (SQLException sqle) { throw new DataAccessException(sqle); } catch (DataAccessException dae) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java index 53cc13c1dd96..e9970c51c387 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java @@ -23,7 +23,7 @@ */ public class HistoryQuery { - private String userName; + private String userIdentity; private String sourceId; private Date startDate; private Date endDate; @@ -56,12 +56,12 @@ public void setStartDate(Date startDate) { this.startDate = startDate; } - public String getUserName() { - return userName; + public String getUserIdentity() { + return userIdentity; } - public void setUserName(String userName) { - this.userName = userName; + public void setUserIdentity(String userIdentity) { + this.userIdentity = userIdentity; } public Integer getOffset() { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java index 6ece5cf1279b..e4cce3e11eec 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java @@ -25,7 +25,7 @@ public class PreviousValue { private String previousValue; private Date timestamp; - private String userName; + private String userIdentity; public String getPreviousValue() { return previousValue; @@ -43,12 +43,12 @@ public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } - public String getUserName() { - return userName; + public String getUserIdentity() { + return userIdentity; } - public void setUserName(String userName) { - this.userName = userName; + public void setUserIdentity(String userIdentity) { + this.userIdentity = userIdentity; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java index 5962cc2a93f4..fa334c4d5d70 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java @@ -51,21 +51,6 @@ public void setIdentity(String identity) { this.identity = identity; } - /** - * @return the username - */ - @ApiModelProperty( - value = "The username.", - readOnly = true - ) - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - /** * @return the user access status */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PreviousValueDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PreviousValueDTO.java index 132456ca937e..558f471e7ec2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PreviousValueDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PreviousValueDTO.java @@ -17,10 +17,11 @@ package org.apache.nifi.web.api.dto; import com.wordnik.swagger.annotations.ApiModelProperty; -import java.util.Date; +import org.apache.nifi.web.api.dto.util.DateTimeAdapter; + import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.apache.nifi.web.api.dto.util.DateTimeAdapter; +import java.util.Date; /** * The previous value for a processor property. @@ -30,7 +31,7 @@ public class PreviousValueDTO { private String previousValue; private Date timestamp; - private String userName; + private String userIdentity; /** * @return previous value @@ -67,11 +68,11 @@ public void setTimestamp(Date timestamp) { @ApiModelProperty( value = "The user who changed the previous value." ) - public String getUserName() { - return userName; + public String getUserIdentity() { + return userIdentity; } - public void setUserName(String userName) { - this.userName = userName; + public void setUserIdentity(String userIdentity) { + this.userIdentity = userIdentity; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/ActionDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/ActionDTO.java index d4dea387655d..1332f5cceb40 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/ActionDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/ActionDTO.java @@ -17,13 +17,14 @@ package org.apache.nifi.web.api.dto.action; import com.wordnik.swagger.annotations.ApiModelProperty; -import java.util.Date; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.apache.nifi.web.api.dto.action.component.details.ComponentDetailsDTO; import org.apache.nifi.web.api.dto.action.details.ActionDetailsDTO; import org.apache.nifi.web.api.dto.util.DateTimeAdapter; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Date; + /** * An action performed in this NiFi. */ @@ -32,7 +33,6 @@ public class ActionDTO { private Integer id; private String userDn; - private String userName; private Date timestamp; private String sourceId; @@ -71,20 +71,6 @@ public void setUserDn(String userDn) { this.userDn = userDn; } - /** - * @return user name who perform this action - */ - @ApiModelProperty( - value = "The name of the user that performed the action." - ) - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - /** * @return action's timestamp */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/HistoryQueryDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/HistoryQueryDTO.java index 7d4d21d61ad2..8dde36198372 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/HistoryQueryDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/HistoryQueryDTO.java @@ -17,10 +17,11 @@ package org.apache.nifi.web.api.dto.action; import com.wordnik.swagger.annotations.ApiModelProperty; -import java.util.Date; +import org.apache.nifi.web.api.dto.util.DateTimeAdapter; + import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.apache.nifi.web.api.dto.util.DateTimeAdapter; +import java.util.Date; /** * A history query to find desired actions. @@ -28,7 +29,7 @@ @XmlType(name = "historyQuery") public class HistoryQueryDTO { - private String userName; + private String userIdentity; private String sourceId; private Date startDate; private Date endDate; @@ -38,17 +39,17 @@ public class HistoryQueryDTO { private String sortOrder; /** - * @return user name + * @return user identity */ @ApiModelProperty( - value = "The name of the source component." + value = "The user identity." ) - public String getUserName() { - return userName; + public String getUserIdentity() { + return userIdentity; } - public void setUserName(String userName) { - this.userName = userName; + public void setUserIdentity(String userIdentity) { + this.userIdentity = userIdentity; } /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserGroupSearchResultDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserGroupSearchResultDTO.java deleted file mode 100644 index ef81b887aa46..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserGroupSearchResultDTO.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.api.dto.search; - -import com.wordnik.swagger.annotations.ApiModelProperty; -import javax.xml.bind.annotation.XmlType; - -/** - * A search result for a matching user group. - */ -@XmlType(name = "userGroupSearchResult") -public class UserGroupSearchResultDTO { - - private String group; - - /** - * @return name of the group that matched - */ - @ApiModelProperty( - value = "The name of the group that matched the search." - ) - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserSearchResultDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserSearchResultDTO.java deleted file mode 100644 index 8bed771b4728..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/search/UserSearchResultDTO.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.api.dto.search; - -import com.wordnik.swagger.annotations.ApiModelProperty; -import javax.xml.bind.annotation.XmlType; - -/** - * A search result for a matching user. - */ -@XmlType(name = "userSearchResult") -public class UserSearchResultDTO { - - private String userName; - private String userDn; - - /** - * @return dn of the user that matched - */ - @ApiModelProperty( - value = "The dn of the user that matched the search." - ) - public String getUserDn() { - return userDn; - } - - public void setUserDn(String userDn) { - this.userDn = userDn; - } - - /** - * @return username of user that matched - */ - @ApiModelProperty( - value = "The name of the user that matched the search." - ) - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AuthorityEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AuthorityEntity.java deleted file mode 100644 index 9b023ba3024c..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/AuthorityEntity.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.api.entity; - -import java.util.Set; - -import javax.xml.bind.annotation.XmlRootElement; - -/** - * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds a set of user authorities. - */ -@XmlRootElement(name = "authoritiesEntity") -public class AuthorityEntity extends Entity { - - private String userId; - private Set authorities; - - /** - * @return current user id - */ - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - /** - * @return set of authorities that are being serialized - */ - public Set getAuthorities() { - return authorities; - } - - public void setAuthorities(Set authorities) { - this.authorities = authorities; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java similarity index 50% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java index ee38efef8cc8..1a7ff1c69f95 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java @@ -16,28 +16,23 @@ */ package org.apache.nifi.web.api.entity; +import org.apache.nifi.web.api.dto.AccessPolicyDTO; + import javax.xml.bind.annotation.XmlRootElement; /** * A serialized representation of this class can be placed in the entity body of a response to the API. This particular entity holds the users identity. */ -@XmlRootElement(name = "identityEntity") -public class IdentityEntity extends Entity { +@XmlRootElement(name = "currentEntity") +public class CurrentUserEntity extends Entity { - private String userId; private String identity; private boolean anonymous; - /** - * @return current user id - */ - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } + private AccessPolicyDTO provenancePermissions; + private AccessPolicyDTO countersPermissions; + private AccessPolicyDTO tenantsPermissions; + private AccessPolicyDTO controllerPermissions; /** * @return the user identity being serialized @@ -60,4 +55,48 @@ public boolean isAnonymous() { public void setAnonymous(boolean anonymous) { this.anonymous = anonymous; } + + /** + * @return if the use can query provenance + */ + public AccessPolicyDTO getProvenancePermissions() { + return provenancePermissions; + } + + public void setProvenancePermissions(AccessPolicyDTO provenancePermissions) { + this.provenancePermissions = provenancePermissions; + } + + /** + * @return permissions for accessing counters + */ + public AccessPolicyDTO getCountersPermissions() { + return countersPermissions; + } + + public void setCountersPermissions(AccessPolicyDTO countersPermissions) { + this.countersPermissions = countersPermissions; + } + + /** + * @return permissions for accessing users + */ + public AccessPolicyDTO getTenantsPermissions() { + return tenantsPermissions; + } + + public void setTenantsPermissions(AccessPolicyDTO tenantsPermissions) { + this.tenantsPermissions = tenantsPermissions; + } + + /** + * @return permissions for accessing the controller + */ + public AccessPolicyDTO getControllerPermissions() { + return controllerPermissions; + } + + public void setControllerPermissions(AccessPolicyDTO controllerPermissions) { + this.controllerPermissions = controllerPermissions; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplateEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplateEntity.java index 553d686a270d..483de4face98 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplateEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplateEntity.java @@ -16,14 +16,15 @@ */ package org.apache.nifi.web.api.entity; -import javax.xml.bind.annotation.XmlRootElement; import org.apache.nifi.web.api.dto.TemplateDTO; +import javax.xml.bind.annotation.XmlRootElement; + /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a TemplateDTO. */ @XmlRootElement(name = "templateEntity") -public class TemplateEntity extends Entity { +public class TemplateEntity extends ComponentEntity { private TemplateDTO template; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplatesEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplatesEntity.java index 3a9f46d70b1f..42ea3cdb9c48 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplatesEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/TemplatesEntity.java @@ -16,12 +16,12 @@ */ package org.apache.nifi.web.api.entity; -import java.util.Date; -import java.util.Set; +import org.apache.nifi.web.api.dto.util.TimeAdapter; + import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.apache.nifi.web.api.dto.TemplateDTO; -import org.apache.nifi.web.api.dto.util.TimeAdapter; +import java.util.Date; +import java.util.Set; /** * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a set of TemplateDTOs. @@ -29,7 +29,7 @@ @XmlRootElement(name = "templatesEntity") public class TemplatesEntity extends Entity { - private Set templates; + private Set templates; private Date generated; /** @@ -37,11 +37,11 @@ public class TemplatesEntity extends Entity { * * @return The TemplateDTO object */ - public Set getTemplates() { + public Set getTemplates() { return templates; } - public void setTemplates(Set templates) { + public void setTemplates(Set templates) { this.templates = templates; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UserSearchResultsEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UserSearchResultsEntity.java deleted file mode 100644 index fcdeee3a2083..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/UserSearchResultsEntity.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.api.entity; - -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; -import org.apache.nifi.web.api.dto.search.UserGroupSearchResultDTO; -import org.apache.nifi.web.api.dto.search.UserSearchResultDTO; - -/** - * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to UserSearchResultDTOs and - * UserGroupSearchResultDTOs. - */ -@XmlRootElement(name = "userSearchResultsEntity") -public class UserSearchResultsEntity { - - private List userResults; - private List userGroupResults; - - /** - * @return user search results - */ - public List getUserResults() { - return userResults; - } - - public void setUserResults(List userResults) { - this.userResults = userResults; - } - - /** - * @return user group search results - */ - public List getUserGroupResults() { - return userGroupResults; - } - - public void setUserGroupResults(List userGroupResults) { - this.userGroupResults = userGroupResults; - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java index 7c04298175b8..e49e24ea9ae0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/FileAuthorizer.java @@ -69,7 +69,7 @@ import java.util.zip.GZIPInputStream; /** - * Provides identity checks and grants authorities. + * Provides authorizes requests to resources using policies persisted in a file. */ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUserUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUserUtils.java index 840df69005e4..4a752748e4db 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUserUtils.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUserUtils.java @@ -47,13 +47,13 @@ public static NiFiUser getNiFiUser() { return user; } - public static String getNiFiUserName() { + public static String getNiFiUserIdentity() { // get the nifi user to extract the username NiFiUser user = NiFiUserUtils.getNiFiUser(); if (user == null) { return "unknown"; } else { - return user.getUserName(); + return user.getIdentity(); } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java index 8c41a1fb4f86..8d501515098b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java @@ -16,32 +16,24 @@ */ package org.apache.nifi.authorization.user; -import java.io.Serializable; import java.util.Objects; /** * An implementation of NiFiUser. */ -public class StandardNiFiUser implements NiFiUser, Serializable { - private static final long serialVersionUID = -5503790026187817496L; +public class StandardNiFiUser implements NiFiUser { public static final StandardNiFiUser ANONYMOUS = new StandardNiFiUser("anonymous"); private final String identity; - private final String userName; private final NiFiUser chain; public StandardNiFiUser(String identity) { - this(identity, identity, null); + this(identity, null); } public StandardNiFiUser(String identity, NiFiUser chain) { - this(identity, identity, chain); - } - - public StandardNiFiUser(String identity, String userName, NiFiUser chain) { this.identity = identity; - this.userName = userName; this.chain = chain; } @@ -51,11 +43,6 @@ public String getIdentity() { return identity; } - @Override - public String getUserName() { - return userName; - } - @Override public NiFiUser getChain() { return chain; @@ -89,6 +76,6 @@ public int hashCode() { @Override public String toString() { - return String.format("identity[%s], userName[%s]", getIdentity(), getUserName(), ", "); + return String.format("identity[%s]", getIdentity()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java index b330581a6f26..5231095feb12 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java @@ -16,9 +16,6 @@ */ package org.apache.nifi.controller; -import java.util.HashSet; -import java.util.Set; - import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; import org.apache.nifi.authorization.AuthorizationResult; @@ -32,6 +29,7 @@ import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.connectable.Connection; import org.apache.nifi.controller.label.Label; +import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.groups.RemoteProcessGroup; import org.apache.nifi.web.api.dto.ConnectionDTO; @@ -43,6 +41,9 @@ import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.TemplateDTO; +import java.util.HashSet; +import java.util.Set; + public class Template implements Authorizable { private final TemplateDTO dto; @@ -84,53 +85,68 @@ public Resource getResource() { return ResourceFactory.getComponentResource(ResourceType.Template, dto.getId(), dto.getName()); } + private ProcessGroup getRootGroup(final ProcessGroup currentGroup) { + if (currentGroup.getParent() == null) { + return currentGroup; + } else { + return getRootGroup(currentGroup.getParent()); + } + } + private Set getAuthorizableComponents() { - return getAuthorizableComponents(processGroup); + return getAuthorizableComponents(processGroup.getIdentifier(), dto.getSnippet()); } - private Set getAuthorizableComponents(final ProcessGroup processGroup) { + private Set getAuthorizableComponents(final String currentGroupId, final FlowSnippetDTO snippet) { final Set authComponents = new HashSet<>(); - final FlowSnippetDTO snippet = dto.getSnippet(); - authComponents.add(processGroup); + // If there is any component in the DTO that still exists in the flow, check its authorizations... + // need to go to the root group in case a sensitive processor was moved out of this processGroup + final ProcessGroup root = getRootGroup(processGroup); + + // include the current group + final ProcessGroup currentGroup = root.findProcessGroup(currentGroupId); + authComponents.add(currentGroup); - // If there is any component in the DTO that still exists in the flow, check its authorizations for (final ConnectionDTO connectionDto : snippet.getConnections()) { - final Connection connection = processGroup.getConnection(connectionDto.getId()); + final Connection connection = root.findConnection(connectionDto.getId()); if (connection != null) { authComponents.add(connection); } } - // TODO: Authorize Controller Services for (final ControllerServiceDTO service : snippet.getControllerServices()) { + final ControllerServiceNode controllerService = root.findControllerService(service.getId()); + if (controllerService != null) { + authComponents.add(controllerService); + } } for (final LabelDTO labelDto : snippet.getLabels()) { - final Label label = processGroup.getLabel(labelDto.getId()); + final Label label = root.findLabel(labelDto.getId()); if (label != null) { authComponents.add(label); } } for (final ProcessorDTO processorDto : snippet.getProcessors()) { - final ProcessorNode procNode = processGroup.getProcessor(processorDto.getId()); + final ProcessorNode procNode = root.findProcessor(processorDto.getId()); if (procNode != null) { authComponents.add(procNode); } } for (final RemoteProcessGroupDTO groupDto : snippet.getRemoteProcessGroups()) { - final RemoteProcessGroup rpg = processGroup.getRemoteProcessGroup(groupDto.getId()); + final RemoteProcessGroup rpg = root.findRemoteProcessGroup(groupDto.getId()); if (rpg != null) { authComponents.add(rpg); } } for (final ProcessGroupDTO groupDto : snippet.getProcessGroups()) { - final ProcessGroup group = processGroup.getProcessGroup(groupDto.getId()); + final ProcessGroup group = root.findProcessGroup(groupDto.getId()); if (group != null) { - authComponents.addAll(getAuthorizableComponents(group)); + authComponents.addAll(getAuthorizableComponents(groupDto.getId(), groupDto.getContents())); } } @@ -170,12 +186,15 @@ private AuthorizationResult checkAuthorization(final Authorizer authorizer, fina if (Result.ResourceNotFound.equals(result.getResult())) { for (final Authorizable child : getAuthorizableComponents()) { final AuthorizationResult childResult = child.checkAuthorization(authorizer, action, user); - if (Result.Denied.equals(childResult)) { + + // if the authoriable in this template explicitly says no, respect it + if (Result.Denied.equals(childResult.getResult())) { return childResult; } } - return AuthorizationResult.denied(); + // if all authorizables are approved or no longer have a policy, approve it + return AuthorizationResult.approved(); } else { return result; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java index 3d2eca29366b..d49aa680e271 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java @@ -16,39 +16,7 @@ */ package org.apache.nifi.controller; -import static java.util.Objects.requireNonNull; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.LockSupport; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import javax.net.ssl.SSLContext; - +import com.sun.jersey.api.client.ClientHandlerException; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.action.Action; import org.apache.nifi.admin.service.AuditService; @@ -182,10 +150,10 @@ import org.apache.nifi.processor.SimpleProcessLogger; import org.apache.nifi.processor.StandardProcessorInitializationContext; import org.apache.nifi.processor.StandardValidationContextFactory; +import org.apache.nifi.provenance.ProvenanceAuthorizableFactory; import org.apache.nifi.provenance.ProvenanceEventRecord; import org.apache.nifi.provenance.ProvenanceEventRepository; import org.apache.nifi.provenance.ProvenanceEventType; -import org.apache.nifi.provenance.ProvenanceAuthorizableFactory; import org.apache.nifi.provenance.StandardProvenanceEventRecord; import org.apache.nifi.remote.HttpRemoteSiteListener; import org.apache.nifi.remote.RemoteGroupPort; @@ -234,7 +202,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jersey.api.client.ClientHandlerException; +import javax.net.ssl.SSLContext; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static java.util.Objects.requireNonNull; public class FlowController implements EventAccess, ControllerServiceProvider, ReportingTaskProvider, QueueProvider, Authorizable, ProvenanceAuthorizableFactory { @@ -3863,13 +3861,19 @@ public Authorizable createProvenanceAuthorizable(final String componentId) { if (rootGroupId.equals(componentId)) { authorizable = new ProvenanceEventAuthorizable(rootGroup); } else { - final Connectable connectable = rootGroup.findConnectable(componentId); + Authorizable authorizableComponent = rootGroup.findConnectable(componentId); + + // if its not a connectable, consider connections (for drop events) + if (authorizableComponent == null) { + authorizableComponent = rootGroup.findConnection(componentId); + } - if (connectable == null) { + // if still not found, report it + if (authorizableComponent == null) { throw new ResourceNotFoundException("The component that generated this event is no longer part of the data flow."); } - authorizable = new ProvenanceEventAuthorizable(connectable); + authorizable = new ProvenanceEventAuthorizable(authorizableComponent); } return authorizable; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java index 6b643e6e5e84..a931841294fd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java @@ -2249,6 +2249,10 @@ public void verifyCanDelete(final boolean ignoreConnections) { childGroup.verifyCanDelete(true); } + if (!templates.isEmpty()) { + throw new IllegalStateException(String.format("Cannot delete Process Group because it contains %s Templates. The Templates must be deleted first.", templates.size())); + } + if (!ignoreConnections) { for (final Port port : inputPorts.values()) { for (final Connection connection : port.getIncomingConnections()) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/FlowChangeAction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/FlowChangeAction.java index ad220594e65a..4d8d1d00445e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/FlowChangeAction.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/FlowChangeAction.java @@ -27,7 +27,6 @@ public class FlowChangeAction implements Action { private Integer id; private String userIdentity; - private String userName; private Date timestamp; private String sourceId; @@ -65,15 +64,6 @@ public void setUserIdentity(String userIdentity) { this.userIdentity = userIdentity; } - @Override - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - @Override public String getSourceId() { return sourceId; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ComponentStateAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ComponentStateAuditor.java index e231456fcd66..2f707963b6f9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ComponentStateAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ComponentStateAuditor.java @@ -73,7 +73,6 @@ public StateMap clearProcessorStateAdvice(ProceedingJoinPoint proceedingJoinPoin // create the clear action FlowChangeAction configAction = new FlowChangeAction(); configAction.setUserIdentity(user.getIdentity()); - configAction.setUserName(user.getUserName()); configAction.setOperation(Operation.ClearState); configAction.setTimestamp(new Date()); configAction.setSourceId(processor.getIdentifier()); @@ -120,7 +119,6 @@ public StateMap clearControllerServiceStateAdvice(ProceedingJoinPoint proceeding // create the clear action FlowChangeAction configAction = new FlowChangeAction(); configAction.setUserIdentity(user.getIdentity()); - configAction.setUserName(user.getUserName()); configAction.setOperation(Operation.ClearState); configAction.setTimestamp(new Date()); configAction.setSourceId(controllerService.getIdentifier()); @@ -167,7 +165,6 @@ public StateMap clearReportingTaskStateAdvice(ProceedingJoinPoint proceedingJoin // create the clear action FlowChangeAction configAction = new FlowChangeAction(); configAction.setUserIdentity(user.getIdentity()); - configAction.setUserName(user.getUserName()); configAction.setOperation(Operation.ClearState); configAction.setTimestamp(new Date()); configAction.setSourceId(reportingTask.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java index d8c27360b470..01f6d702f2bb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerAuditor.java @@ -80,7 +80,6 @@ public void updateControllerTimerDrivenThreadsAdvice(ProceedingJoinPoint proceed // create the config action FlowChangeAction configAction = new FlowChangeAction(); configAction.setUserIdentity(user.getIdentity()); - configAction.setUserName(user.getUserName()); configAction.setOperation(Operation.Configure); configAction.setTimestamp(new Date()); configAction.setSourceId("Flow Controller"); @@ -133,7 +132,6 @@ public void updateControllerEventDrivenThreadsAdvice(ProceedingJoinPoint proceed // create the config action FlowChangeAction configAction = new FlowChangeAction(); configAction.setUserIdentity(user.getIdentity()); - configAction.setUserName(user.getUserName()); configAction.setOperation(Operation.Configure); configAction.setTimestamp(new Date()); configAction.setSourceId("Flow Controller"); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java index ded90f868b59..a12298319216 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java @@ -16,13 +16,6 @@ */ package org.apache.nifi.audit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -30,13 +23,13 @@ import org.apache.nifi.action.component.details.FlowChangeExtensionDetails; import org.apache.nifi.action.details.ActionDetails; import org.apache.nifi.action.details.FlowChangeConfigureDetails; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.ScheduledState; -import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.controller.service.ControllerServiceReference; import org.apache.nifi.controller.service.ControllerServiceState; @@ -49,6 +42,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + /** * Audits controller service creation/removal and configuration changes. */ @@ -168,7 +168,6 @@ public Object updateControllerServiceAdvice(ProceedingJoinPoint proceedingJoinPo // create a configuration action FlowChangeAction configurationAction = new FlowChangeAction(); configurationAction.setUserIdentity(user.getIdentity()); - configurationAction.setUserName(user.getUserName()); configurationAction.setOperation(operation); configurationAction.setTimestamp(actionTimestamp); configurationAction.setSourceId(controllerService.getIdentifier()); @@ -188,7 +187,6 @@ public Object updateControllerServiceAdvice(ProceedingJoinPoint proceedingJoinPo // create a controller service action FlowChangeAction serviceAction = new FlowChangeAction(); serviceAction.setUserIdentity(user.getIdentity()); - serviceAction.setUserName(user.getUserName()); serviceAction.setTimestamp(new Date()); serviceAction.setSourceId(controllerService.getIdentifier()); serviceAction.setSourceName(controllerService.getName()); @@ -272,7 +270,6 @@ private void getUpdateActionsForReferencingComponents( // create a processor action FlowChangeAction processorAction = new FlowChangeAction(); processorAction.setUserIdentity(user.getIdentity()); - processorAction.setUserName(user.getUserName()); processorAction.setTimestamp(new Date()); processorAction.setSourceId(processor.getIdentifier()); processorAction.setSourceName(processor.getName()); @@ -290,7 +287,6 @@ private void getUpdateActionsForReferencingComponents( // create a reporting task action FlowChangeAction reportingTaskAction = new FlowChangeAction(); reportingTaskAction.setUserIdentity(user.getIdentity()); - reportingTaskAction.setUserName(user.getUserName()); reportingTaskAction.setTimestamp(new Date()); reportingTaskAction.setSourceId(reportingTask.getIdentifier()); reportingTaskAction.setSourceName(reportingTask.getName()); @@ -308,7 +304,6 @@ private void getUpdateActionsForReferencingComponents( // create a controller service action FlowChangeAction serviceAction = new FlowChangeAction(); serviceAction.setUserIdentity(user.getIdentity()); - serviceAction.setUserName(user.getUserName()); serviceAction.setTimestamp(new Date()); serviceAction.setSourceId(controllerService.getIdentifier()); serviceAction.setSourceName(controllerService.getName()); @@ -388,7 +383,6 @@ private Action generateAuditRecord(ControllerServiceNode controllerService, Oper // create the controller service action for adding this controller service action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(controllerService.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java index 7230f9ce8e48..4f96772e3c66 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java @@ -16,24 +16,23 @@ */ package org.apache.nifi.audit; -import java.util.Date; - import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; import org.apache.nifi.action.Operation; import org.apache.nifi.action.details.ActionDetails; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.connectable.Funnel; -import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.web.dao.FunnelDAO; - import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Date; + @Aspect public class FunnelAuditor extends NiFiAuditor { @@ -121,7 +120,6 @@ public Action generateAuditRecord(Funnel funnel, Operation operation, ActionDeta // create the action for adding this funnel action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(funnel.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java index 3e5b0b57f1d0..93ac1f7610c0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java @@ -204,7 +204,6 @@ public Port updatePortAdvice(ProceedingJoinPoint proceedingJoinPoint, PortDTO po // create the port action for updating the name FlowChangeAction portAction = new FlowChangeAction(); portAction.setUserIdentity(user.getIdentity()); - portAction.setUserName(user.getUserName()); portAction.setOperation(Operation.Configure); portAction.setTimestamp(timestamp); portAction.setSourceId(updatedPort.getIdentifier()); @@ -224,7 +223,6 @@ public Port updatePortAdvice(ProceedingJoinPoint proceedingJoinPoint, PortDTO po // create a processor action FlowChangeAction processorAction = new FlowChangeAction(); processorAction.setUserIdentity(user.getIdentity()); - processorAction.setUserName(user.getUserName()); processorAction.setTimestamp(new Date()); processorAction.setSourceId(updatedPort.getIdentifier()); processorAction.setSourceName(updatedPort.getName()); @@ -321,7 +319,6 @@ public Action generateAuditRecord(Port port, Operation operation, ActionDetails // create the port action for adding this processor action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(port.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java index a4277c6ae64a..391f9e226d8e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java @@ -143,7 +143,6 @@ public ProcessGroup updateProcessGroupAdvice(ProceedingJoinPoint proceedingJoinP // create the port action for updating the name FlowChangeAction processGroupAction = new FlowChangeAction(); processGroupAction.setUserIdentity(user.getIdentity()); - processGroupAction.setUserName(user.getUserName()); processGroupAction.setOperation(operation); processGroupAction.setTimestamp(timestamp); processGroupAction.setSourceId(updatedProcessGroup.getIdentifier()); @@ -188,7 +187,6 @@ public void scheduleComponentsAdvice(ProceedingJoinPoint proceedingJoinPoint, St // if the user was starting/stopping this process group FlowChangeAction action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setSourceId(processGroup.getIdentifier()); action.setSourceName(processGroup.getName()); action.setSourceType(Component.ProcessGroup); @@ -264,7 +262,6 @@ public Action generateAuditRecord(ProcessGroup processGroup, Operation operation // create the process group action for adding this process group action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(processGroup.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java index 8da70f05f729..32aab33c8a0c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java @@ -16,17 +16,7 @@ */ package org.apache.nifi.audit; -import java.text.Collator; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -34,22 +24,32 @@ import org.apache.nifi.action.component.details.FlowChangeExtensionDetails; import org.apache.nifi.action.details.ActionDetails; import org.apache.nifi.action.details.FlowChangeConfigureDetails; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.processor.Relationship; -import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.web.api.dto.ProcessorConfigDTO; import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.dao.ProcessorDAO; -import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + /** * Audits processor creation/removal and configuration changes. */ @@ -177,7 +177,6 @@ public ProcessorNode updateProcessorAdvice(ProceedingJoinPoint proceedingJoinPoi // create a configuration action FlowChangeAction configurationAction = new FlowChangeAction(); configurationAction.setUserIdentity(user.getIdentity()); - configurationAction.setUserName(user.getUserName()); configurationAction.setOperation(operation); configurationAction.setTimestamp(actionTimestamp); configurationAction.setSourceId(processor.getIdentifier()); @@ -197,7 +196,6 @@ public ProcessorNode updateProcessorAdvice(ProceedingJoinPoint proceedingJoinPoi // create a processor action FlowChangeAction processorAction = new FlowChangeAction(); processorAction.setUserIdentity(user.getIdentity()); - processorAction.setUserName(user.getUserName()); processorAction.setTimestamp(new Date()); processorAction.setSourceId(processor.getIdentifier()); processorAction.setSourceName(processor.getName()); @@ -293,7 +291,6 @@ public Action generateAuditRecord(ProcessorNode processor, Operation operation, // create the processor action for adding this processor action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(processor.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java index 1780790d2689..2560b2459077 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java @@ -16,14 +16,7 @@ */ package org.apache.nifi.audit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -32,28 +25,34 @@ import org.apache.nifi.action.details.ConnectDetails; import org.apache.nifi.action.details.FlowChangeConfigureDetails; import org.apache.nifi.action.details.FlowChangeConnectDetails; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.connectable.Connectable; import org.apache.nifi.connectable.Connection; import org.apache.nifi.connectable.Funnel; import org.apache.nifi.connectable.Port; import org.apache.nifi.controller.ProcessorNode; -import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.flowfile.FlowFilePrioritizer; +import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.processor.Relationship; import org.apache.nifi.remote.RemoteGroupPort; import org.apache.nifi.remote.TransferDirection; -import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.web.api.dto.ConnectionDTO; import org.apache.nifi.web.dao.ConnectionDAO; - -import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + /** * Audits relationship creation/removal. */ @@ -188,7 +187,6 @@ public Connection updateConnectionAdvice(ProceedingJoinPoint proceedingJoinPoint // create a configuration action FlowChangeAction configurationAction = new FlowChangeAction(); configurationAction.setUserIdentity(user.getIdentity()); - configurationAction.setUserName(user.getUserName()); configurationAction.setOperation(Operation.Configure); configurationAction.setTimestamp(actionTimestamp); configurationAction.setSourceId(connection.getIdentifier()); @@ -352,7 +350,6 @@ public Action generateAuditRecordForConnection(Connection connection, Operation // create a new relationship action action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(actionTimestamp); action.setSourceId(connectionId); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java index e19bf29cf6ae..d9a5df661477 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java @@ -247,7 +247,6 @@ public RemoteProcessGroup auditUpdateProcessGroupConfiguration( // create the port action for updating the name FlowChangeAction remoteProcessGroupAction = new FlowChangeAction(); remoteProcessGroupAction.setUserIdentity(user.getIdentity()); - remoteProcessGroupAction.setUserName(user.getUserName()); remoteProcessGroupAction.setOperation(Operation.Configure); remoteProcessGroupAction.setTimestamp(timestamp); remoteProcessGroupAction.setSourceId(updatedRemoteProcessGroup.getIdentifier()); @@ -268,7 +267,6 @@ public RemoteProcessGroup auditUpdateProcessGroupConfiguration( // create a processor action FlowChangeAction remoteProcessGroupAction = new FlowChangeAction(); remoteProcessGroupAction.setUserIdentity(user.getIdentity()); - remoteProcessGroupAction.setUserName(user.getUserName()); remoteProcessGroupAction.setTimestamp(new Date()); remoteProcessGroupAction.setSourceId(updatedRemoteProcessGroup.getIdentifier()); remoteProcessGroupAction.setSourceName(updatedRemoteProcessGroup.getName()); @@ -356,7 +354,6 @@ public Action generateAuditRecord(RemoteProcessGroup remoteProcessGroup, Operati // create the remote process group action action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(remoteProcessGroup.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java index 0dc8ee308b4d..e198d5e17c1c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java @@ -16,13 +16,6 @@ */ package org.apache.nifi.audit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -30,11 +23,11 @@ import org.apache.nifi.action.component.details.FlowChangeExtensionDetails; import org.apache.nifi.action.details.ActionDetails; import org.apache.nifi.action.details.FlowChangeConfigureDetails; +import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.ScheduledState; -import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.web.api.dto.ReportingTaskDTO; import org.apache.nifi.web.dao.ReportingTaskDAO; import org.aspectj.lang.ProceedingJoinPoint; @@ -43,6 +36,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + /** * Audits reporting creation/removal and configuration changes. */ @@ -161,7 +161,6 @@ public Object updateReportingTaskAdvice(ProceedingJoinPoint proceedingJoinPoint, // create a configuration action FlowChangeAction configurationAction = new FlowChangeAction(); configurationAction.setUserIdentity(user.getIdentity()); - configurationAction.setUserName(user.getUserName()); configurationAction.setOperation(operation); configurationAction.setTimestamp(actionTimestamp); configurationAction.setSourceId(reportingTask.getIdentifier()); @@ -181,7 +180,6 @@ public Object updateReportingTaskAdvice(ProceedingJoinPoint proceedingJoinPoint, // create a reporting task action FlowChangeAction taskAction = new FlowChangeAction(); taskAction.setUserIdentity(user.getIdentity()); - taskAction.setUserName(user.getUserName()); taskAction.setTimestamp(new Date()); taskAction.setSourceId(reportingTask.getIdentifier()); taskAction.setSourceName(reportingTask.getName()); @@ -277,7 +275,6 @@ public Action generateAuditRecord(ReportingTaskNode reportingTask, Operation ope // create the reporting task action for adding this reporting task action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(new Date()); action.setSourceId(reportingTask.getIdentifier()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java index 27b76b1e2343..ce7313e404ee 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java @@ -232,7 +232,6 @@ private FlowChangeAction generateAuditRecord(String id, String name, Component t // create the action for adding this funnel action = new FlowChangeAction(); action.setUserIdentity(user.getIdentity()); - action.setUserName(user.getUserName()); action.setOperation(operation); action.setTimestamp(timestamp); action.setSourceId(id); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java index f5421e50a7b7..01f06c2abad7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/AuthorizableLookup.java @@ -37,6 +37,20 @@ public interface AuthorizableLookup { */ Authorizable getProcessor(String id); + /** + * Get the authorizable for querying Provenance. + * + * @return authorizable + */ + Authorizable getProvenance(); + + /** + * Get the authorizable for viewing/reseting Counters. + * + * @return authorizable + */ + Authorizable getCounters(); + /** * Get the authorizable InputPort. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index 40361efcd7ba..dd1f7e03cff2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -73,6 +73,7 @@ import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity; +import org.apache.nifi.web.api.entity.CurrentUserEntity; import org.apache.nifi.web.api.entity.FlowConfigurationEntity; import org.apache.nifi.web.api.entity.FlowEntity; import org.apache.nifi.web.api.entity.FunnelEntity; @@ -86,6 +87,7 @@ import org.apache.nifi.web.api.entity.ReportingTaskEntity; import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import org.apache.nifi.web.api.entity.SnippetEntity; +import org.apache.nifi.web.api.entity.TemplateEntity; import org.apache.nifi.web.api.entity.UserEntity; import org.apache.nifi.web.api.entity.UserGroupEntity; @@ -440,7 +442,7 @@ public interface NiFiServiceFacade { * * @return templates */ - Set getTemplates(); + Set getTemplates(); /** * Deletes the specified template. @@ -832,9 +834,16 @@ public interface NiFiServiceFacade { */ PortEntity deleteOutputPort(Revision revision, String outputPortId); + // ------------ + // Current user + // ------------ + + CurrentUserEntity getCurrentUser(); + // ---------------------------------------- // Flow methods // ---------------------------------------- + /** * Returns the flow. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java index e9117a9375f8..5a9de3ee49c4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java @@ -20,7 +20,6 @@ import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationProvider; -import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter; import org.apache.nifi.web.security.otp.OtpAuthenticationFilter; import org.apache.nifi.web.security.otp.OtpAuthenticationProvider; import org.apache.nifi.web.security.x509.X509AuthenticationFilter; @@ -54,8 +53,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte private NiFiProperties properties; - private NodeAuthorizedUserFilter nodeAuthorizedUserFilter; - private X509AuthenticationFilter x509AuthenticationFilter; private X509CertificateExtractor certificateExtractor; private X509PrincipalExtractor principalExtractor; @@ -94,9 +91,6 @@ protected void configure(HttpSecurity http) throws Exception { .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); - // cluster authorized user - http.addFilterBefore(nodeAuthorizedUserFilterBean(), AnonymousAuthenticationFilter.class); - // x509 http.addFilterBefore(x509FilterBean(), AnonymousAuthenticationFilter.class); @@ -125,17 +119,6 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { .authenticationProvider(otpAuthenticationProvider); } - @Bean - public NodeAuthorizedUserFilter nodeAuthorizedUserFilterBean() throws Exception { - if (nodeAuthorizedUserFilter == null) { - nodeAuthorizedUserFilter = new NodeAuthorizedUserFilter(); - nodeAuthorizedUserFilter.setProperties(properties); - nodeAuthorizedUserFilter.setCertificateExtractor(certificateExtractor); - nodeAuthorizedUserFilter.setCertificateIdentityProvider(certificateIdentityProvider); - } - return nodeAuthorizedUserFilter; - } - @Bean public JwtAuthenticationFilter jwtFilterBean() throws Exception { if (jwtAuthenticationFilter == null) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java index 13a5c20c3a4e..413988823afc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardAuthorizableLookup.java @@ -16,9 +16,11 @@ */ package org.apache.nifi.web; +import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.resource.AccessPoliciesAuthorizable; import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable; import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.resource.TenantAuthorizable; import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.Snippet; @@ -46,6 +48,30 @@ class StandardAuthorizableLookup implements AuthorizableLookup { private static final TenantAuthorizable TENANT_AUTHORIZABLE = new TenantAuthorizable(); private static final Authorizable ACCESS_POLICIES_AUTHORIZABLE = new AccessPoliciesAuthorizable(); + private static final Authorizable PROVENANCE_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getProvenanceResource(); + } + }; + + private static final Authorizable COUNTERS_AUTHORIZABLE = new Authorizable() { + @Override + public Authorizable getParentAuthorizable() { + return null; + } + + @Override + public Resource getResource() { + return ResourceFactory.getCountersResource(); + } + }; + // nifi core components private ControllerFacade controllerFacade; @@ -126,6 +152,16 @@ public Authorizable getControllerService(final String id) { return controllerServiceDAO.getControllerService(id); } + @Override + public Authorizable getProvenance() { + return PROVENANCE_AUTHORIZABLE; + } + + @Override + public Authorizable getCounters() { + return COUNTERS_AUTHORIZABLE; + } + @Override public Authorizable getControllerServiceReferencingComponent(String controllerSeriveId, String id) { final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerSeriveId); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java index 169121762493..8a6b4388975e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java @@ -16,22 +16,11 @@ */ package org.apache.nifi.web; -import java.io.Serializable; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.MultivaluedMap; - +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.ClientResponse.Status; +import com.sun.jersey.core.util.MultivaluedMapImpl; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AccessDeniedException; -import org.apache.nifi.authorization.user.NiFiUserDetails; import org.apache.nifi.cluster.coordination.ClusterCoordinator; import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator; import org.apache.nifi.cluster.manager.NodeResponse; @@ -39,13 +28,17 @@ import org.apache.nifi.cluster.protocol.NodeIdentifier; import org.apache.nifi.controller.repository.claim.ContentDirection; import org.apache.nifi.util.NiFiProperties; -import org.apache.nifi.web.util.WebUtils; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.ClientResponse.Status; -import com.sun.jersey.core.util.MultivaluedMapImpl; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.MultivaluedMap; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -87,19 +80,6 @@ public DownloadableContent getContent(final ContentRequestContext request) { headers.put("X-ProxiedEntitiesChain", request.getProxiedEntitiesChain()); } - // add the user's authorities (if any) to the headers - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - final Object userDetailsObj = authentication.getPrincipal(); - if (userDetailsObj instanceof NiFiUserDetails) { - // serialize user details object - final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj); - - // put serialized user details in header - headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails); - } - } - // ensure we were able to detect the cluster node id if (request.getClusterNodeId() == null) { throw new IllegalArgumentException("Unable to determine the which node has the content."); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 180b6bcb43b3..da253cadc832 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -143,6 +143,7 @@ import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity; +import org.apache.nifi.web.api.entity.CurrentUserEntity; import org.apache.nifi.web.api.entity.FlowConfigurationEntity; import org.apache.nifi.web.api.entity.FlowEntity; import org.apache.nifi.web.api.entity.FunnelEntity; @@ -156,6 +157,7 @@ import org.apache.nifi.web.api.entity.ReportingTaskEntity; import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import org.apache.nifi.web.api.entity.SnippetEntity; +import org.apache.nifi.web.api.entity.TemplateEntity; import org.apache.nifi.web.api.entity.TenantEntity; import org.apache.nifi.web.api.entity.UserEntity; import org.apache.nifi.web.api.entity.UserGroupEntity; @@ -639,7 +641,7 @@ public RevisionUpdate update() { final D dto = dtoCreation.apply(component); final Revision updatedRevision = revisionManager.getRevision(revision.getComponentId()).incrementRevision(revision.getClientId()); - final FlowModification lastModification = new FlowModification(updatedRevision, user.getUserName()); + final FlowModification lastModification = new FlowModification(updatedRevision, user.getIdentity()); return new StandardRevisionUpdate<>(dto, lastModification); } }); @@ -1243,7 +1245,7 @@ private RevisionUpdate createComponent(final Revision revision, final controllerFacade.save(); final D dto = dtoCreation.apply(component); - final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getUserName()); + final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity()); return new StandardRevisionUpdate(dto, lastMod); }); } finally { @@ -1271,7 +1273,7 @@ public FunnelEntity createFunnel(final Revision revision, final String groupId, public AccessPolicyEntity createAccessPolicy(final Revision revision, final AccessPolicyDTO accessPolicyDTO) { // TODO read lock on users and groups (and resource+action?) while the policy is being created? final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); - final String creator = NiFiUserUtils.getNiFiUserName(); + final String creator = NiFiUserUtils.getNiFiUserIdentity(); final AccessPolicy newAccessPolicy = accessPolicyDAO.createAccessPolicy(accessPolicyDTO); final AccessPolicyDTO newAccessPolicyDto = dtoFactory.createAccessPolicyDto(newAccessPolicy, newAccessPolicy.getGroups().stream().map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet()), @@ -1288,7 +1290,7 @@ public AccessPolicyEntity createAccessPolicy(final Revision revision, final Acce @Override public UserEntity createUser(final Revision revision, final UserDTO userDTO) { final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); - final String creator = NiFiUserUtils.getNiFiUserName(); + final String creator = NiFiUserUtils.getNiFiUserIdentity(); final User newUser = userDAO.createUser(userDTO); final UserDTO newUserDto = dtoFactory.createUserDto(newUser, newUser.getGroups().stream() .map(mapUserGroupIdToTenantEntity()).collect(Collectors.toSet())); @@ -1300,7 +1302,7 @@ public UserEntity createUser(final Revision revision, final UserDTO userDTO) { @Override public UserGroupEntity createUserGroup(final Revision revision, final UserGroupDTO userGroupDTO) { final Authorizable tenantAuthorizable = authorizableLookup.getTenantAuthorizable(); - final String creator = NiFiUserUtils.getNiFiUserName(); + final String creator = NiFiUserUtils.getNiFiUserIdentity(); final Group newUserGroup = userGroupDAO.createUserGroup(userGroupDTO); final UserGroupDTO newUserGroupDto = dtoFactory.createUserGroupDto(newUserGroup, newUserGroup.getUsers().stream() .map(userId -> { @@ -1615,7 +1617,7 @@ public ControllerServiceEntity createControllerService(final Revision revision, controllerFacade.save(); - final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getUserName()); + final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity()); return new StandardRevisionUpdate(dto, lastMod); }); } finally { @@ -1631,7 +1633,7 @@ public ControllerServiceEntity createControllerService(final Revision revision, controllerFacade.save(); - final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getUserName()); + final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity()); return new StandardRevisionUpdate(dto, lastMod); }); } finally { @@ -1759,7 +1761,7 @@ private ControllerServiceReferencingComponentsEntity createControllerServiceRefe private ControllerServiceReferencingComponentsEntity createControllerServiceReferencingComponentsEntity( final ControllerServiceReference reference, final Map revisions, final Set visited) { - final String modifier = NiFiUserUtils.getNiFiUserName(); + final String modifier = NiFiUserUtils.getNiFiUserIdentity(); final Set referencingComponents = reference.getReferencingComponents(); final Set componentEntities = new HashSet<>(); @@ -1827,7 +1829,7 @@ public ReportingTaskEntity createReportingTask(final Revision revision, final Re controllerFacade.save(); final ReportingTaskDTO dto = dtoFactory.createReportingTaskDto(reportingTask); - final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getUserName()); + final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity()); return new StandardRevisionUpdate(dto, lastMod); }); @@ -1882,7 +1884,6 @@ public void deleteActions(final Date endDate) { // create a purge action to record that records are being removed final FlowChangeAction purgeAction = new FlowChangeAction(); purgeAction.setUserIdentity(user.getIdentity()); - purgeAction.setUserName(user.getUserName()); purgeAction.setOperation(Operation.Purge); purgeAction.setTimestamp(new Date()); purgeAction.setSourceId("Flow Controller"); @@ -2127,12 +2128,18 @@ public TemplateDTO getTemplate(final String id) { } @Override - public Set getTemplates() { - final Set templateDtos = new LinkedHashSet<>(); - for (final Template template : templateDAO.getTemplates()) { - templateDtos.add(dtoFactory.createTemplateDTO(template)); - } - return templateDtos; + public Set getTemplates() { + return templateDAO.getTemplates().stream() + .map(template -> { + final TemplateDTO dto = dtoFactory.createTemplateDTO(template); + final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(template); + + final TemplateEntity entity = new TemplateEntity(); + entity.setId(dto.getId()); + entity.setAccessPolicy(accessPolicy); + entity.setTemplate(dto); + return entity; + }).collect(Collectors.toSet()); } @Override @@ -2598,6 +2605,19 @@ public StatusHistoryDTO getRemoteProcessGroupStatusHistory(final String id) { return controllerFacade.getRemoteProcessGroupStatusHistory(id); } + @Override + public CurrentUserEntity getCurrentUser() { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final CurrentUserEntity entity = new CurrentUserEntity(); + entity.setIdentity(user.getIdentity()); + entity.setAnonymous(user.isAnonymous()); + entity.setProvenancePermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getProvenance())); + entity.setCountersPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getCounters())); + entity.setTenantsPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable())); + entity.setControllerPermissions(dtoFactory.createAccessPolicyDto(authorizableLookup.getController())); + return entity; + } + @Override public ProcessGroupFlowEntity getProcessGroupFlow(final String groupId, final boolean recurse) { return revisionManager.get(groupId, @@ -2823,7 +2843,7 @@ public HistoryDTO getActions(final HistoryQueryDTO historyQueryDto) { historyQuery.setStartDate(historyQueryDto.getStartDate()); historyQuery.setEndDate(historyQueryDto.getEndDate()); historyQuery.setSourceId(historyQueryDto.getSourceId()); - historyQuery.setUserName(historyQueryDto.getUserName()); + historyQuery.setUserIdentity(historyQueryDto.getUserIdentity()); historyQuery.setOffset(historyQueryDto.getOffset()); historyQuery.setCount(historyQueryDto.getCount()); historyQuery.setSortColumn(historyQueryDto.getSortColumn()); @@ -2833,10 +2853,25 @@ public HistoryDTO getActions(final HistoryQueryDTO historyQueryDto) { final History history = auditService.getActions(historyQuery); // only retain authorized actions - history.getActions().stream().filter(action -> authorizeAction(action)).collect(Collectors.toList()); + final HistoryDTO historyDto = dtoFactory.createHistoryDto(history); + if (history.getActions() != null) { + final List actionDtos = new ArrayList<>(); + for (final Action action : history.getActions()) { + if (authorizeAction(action)) { + actionDtos.add(dtoFactory.createActionDto(action)); + } else { + final ActionDTO actionDto = new ActionDTO(); + actionDto.setTimestamp(action.getTimestamp()); + actionDto.setId(action.getId()); + actionDto.setSourceId(action.getSourceId()); + actionDtos.add(actionDto); + } + } + historyDto.setActions(actionDtos); + } // create the response - return dtoFactory.createHistoryDto(history); + return historyDto; } @Override @@ -2869,7 +2904,7 @@ public ComponentHistoryDTO getComponentHistory(final String componentId) { final PreviousValueDTO dto = new PreviousValueDTO(); dto.setPreviousValue(previousValue.getPreviousValue()); dto.setTimestamp(previousValue.getTimestamp()); - dto.setUserName(previousValue.getUserName()); + dto.setUserIdentity(previousValue.getUserIdentity()); previousValueDtos.add(dto); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java index f263ac63c96a..b42f83954230 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java @@ -34,7 +34,6 @@ import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; -import org.apache.nifi.authorization.user.NiFiUserDetails; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.ClusterCoordinator; import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator; @@ -54,16 +53,12 @@ import org.apache.nifi.web.api.entity.ProcessorEntity; import org.apache.nifi.web.api.entity.ReportingTaskEntity; import org.apache.nifi.web.util.ClientResponseUtils; -import org.apache.nifi.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; -import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -182,8 +177,7 @@ public void saveActions(final NiFiWebRequestContext requestContext, final Collec action.setSourceName(configurationAction.getName()); action.setSourceType(componentType); action.setOperation(Operation.Configure); - action.setUserIdentity(getCurrentUserDn()); - action.setUserName(getCurrentUserName()); + action.setUserIdentity(getCurrentUserIdentity()); action.setComponentDetails(extensionDetails); action.setActionDetails(configureDetails); actions.add(action); @@ -203,19 +197,12 @@ public void saveActions(final NiFiWebRequestContext requestContext, final Collec } @Override - public String getCurrentUserDn() { + public String getCurrentUserIdentity() { final NiFiUser user = NiFiUserUtils.getNiFiUser(); authorizeFlowAccess(user); return user.getIdentity(); } - @Override - public String getCurrentUserName() { - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - authorizeFlowAccess(user); - return user.getUserName(); - } - @Override public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException { final String id = requestContext.getId(); @@ -841,19 +828,6 @@ private Map getHeaders(final NiFiWebRequestContext config) { if (StringUtils.isNotBlank(config.getProxiedEntitiesChain())) { headers.put("X-ProxiedEntitiesChain", config.getProxiedEntitiesChain()); } - - // add the user's authorities (if any) to the headers - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - final Object userDetailsObj = authentication.getPrincipal(); - if (userDetailsObj instanceof NiFiUserDetails) { - // serialize user details object - final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj); - - // put serialized user details in header - headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails); - } - } return headers; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java index b42556270884..e0162bcd4500 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java @@ -16,21 +16,11 @@ */ package org.apache.nifi.web.api; -import java.net.URI; -import java.security.cert.X509Certificate; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import io.jsonwebtoken.JwtException; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.admin.service.AdministrationException; import org.apache.nifi.authentication.AuthenticationResponse; @@ -75,12 +65,19 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; -import com.wordnik.swagger.annotations.Api; -import com.wordnik.swagger.annotations.ApiOperation; -import com.wordnik.swagger.annotations.ApiResponse; -import com.wordnik.swagger.annotations.ApiResponses; - -import io.jsonwebtoken.JwtException; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; /** * RESTful endpoint for managing access. @@ -207,7 +204,6 @@ public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) // set the user identity accessStatus.setIdentity(nifiUser.getIdentity()); - accessStatus.setUsername(nifiUser.getUserName()); // attempt authorize to /flow accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); @@ -226,7 +222,6 @@ public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) // set the user identity accessStatus.setIdentity(nifiUser.getIdentity()); - accessStatus.setUsername(nifiUser.getUserName()); // attempt authorize to /flow accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java index 262e2743ba9a..120c387fa8be 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java @@ -25,7 +25,6 @@ import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUser; -import org.apache.nifi.authorization.user.NiFiUserDetails; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.ClusterCoordinator; import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator; @@ -41,12 +40,8 @@ import org.apache.nifi.web.api.dto.SnippetDTO; import org.apache.nifi.web.api.entity.ComponentEntity; import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; -import org.apache.nifi.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -58,7 +53,6 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilderException; import javax.ws.rs.core.UriInfo; -import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -80,13 +74,10 @@ public abstract class ApplicationResource { public static final String VERSION = "version"; public static final String CLIENT_ID = "clientId"; - public static final String CLUSTER_CONTEXT_HTTP_HEADER = "X-ClusterContext"; public static final String PROXY_SCHEME_HTTP_HEADER = "X-ProxyScheme"; public static final String PROXY_HOST_HTTP_HEADER = "X-ProxyHost"; public static final String PROXY_PORT_HTTP_HEADER = "X-ProxyPort"; public static final String PROXY_CONTEXT_PATH_HTTP_HEADER = "X-ProxyContextPath"; - public static final String PROXIED_ENTITIES_CHAIN_HTTP_HEADER = "X-ProxiedEntitiesChain"; - public static final String PROXIED_ENTITY_USER_DETAILS_HTTP_HEADER = "X-ProxiedEntityUserDetails"; private static final Logger logger = LoggerFactory.getLogger(ApplicationResource.class); @@ -324,24 +315,6 @@ protected Map getHeaders(final Map overriddenHea result.put(PROXY_SCHEME_HTTP_HEADER, httpServletRequest.getScheme()); } - if (httpServletRequest.isSecure()) { - - // add the user's authorities (if any) to the headers - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - final Object userDetailsObj = authentication.getPrincipal(); - if (userDetailsObj instanceof NiFiUserDetails) { - // serialize user details object - final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj); - - // put serialized user details in header - result.put(PROXIED_ENTITY_USER_DETAILS_HTTP_HEADER, hexEncodedUserDetails); - - // remove the access token if present, since the user is already authenticated/authorized - result.remove(JwtAuthenticationFilter.AUTHORIZATION); - } - } - } return result; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 30cff5f4f856..31ecd4c4605d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -33,7 +33,6 @@ import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; -import org.apache.nifi.cluster.coordination.node.NodeConnectionState; import org.apache.nifi.controller.FlowController; import org.apache.nifi.web.IllegalClusterResourceRequestException; import org.apache.nifi.web.NiFiServiceFacade; @@ -41,9 +40,7 @@ import org.apache.nifi.web.api.dto.ClusterDTO; import org.apache.nifi.web.api.dto.NodeDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.search.NodeSearchResultDTO; import org.apache.nifi.web.api.entity.ClusterEntity; -import org.apache.nifi.web.api.entity.ClusterSearchResultsEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.HistoryEntity; @@ -69,8 +66,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; -import java.util.ArrayList; -import java.util.List; /** * RESTful endpoint for managing a Flow Controller. @@ -487,81 +482,6 @@ public Response getCluster() { return generateOkResponse(entity).build(); } - /** - * Searches the cluster for a node with a given address. - * - * @param value Search value that will be matched against a node's address - * @return Nodes that match the specified criteria - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("cluster/search-results") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Searches the cluster for a node with the specified address", - response = ClusterSearchResultsEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "DFM", type = "ROLE_DFM"), - @Authorization(value = "Admin", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response searchCluster( - @ApiParam( - value = "Node address to search for.", - required = true - ) - @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { - - authorizeController(RequestAction.READ); - - // ensure connected to the cluster - if (!isConnectedToCluster()) { - throw new IllegalClusterResourceRequestException("Only a node connected to a cluster can process the request."); - } - - final List nodeMatches = new ArrayList<>(); - - // get the nodes in the cluster - final ClusterDTO cluster = serviceFacade.getCluster(); - - // check each to see if it matches the search term - for (NodeDTO node : cluster.getNodes()) { - // ensure the node is connected - if (!NodeConnectionState.CONNECTED.name().equals(node.getStatus())) { - continue; - } - - // determine the current nodes address - final String address = node.getAddress() + ":" + node.getApiPort(); - - // count the node if there is no search or it matches the address - if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(address, value)) { - final NodeSearchResultDTO nodeMatch = new NodeSearchResultDTO(); - nodeMatch.setId(node.getNodeId()); - nodeMatch.setAddress(address); - nodeMatches.add(nodeMatch); - } - } - - // build the response - ClusterSearchResultsEntity results = new ClusterSearchResultsEntity(); - results.setNodeResults(nodeMatches); - - // generate an 200 - OK response - return noCache(Response.ok(results)).build(); - } - /** * Gets the contents of the specified node in this NiFi cluster. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index 5c9bd9a549d0..cc7d5aaade37 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -16,29 +16,13 @@ */ package org.apache.nifi.web.api; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import com.sun.jersey.api.core.ResourceContext; +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; @@ -46,20 +30,25 @@ 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; import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.cluster.coordination.node.NodeConnectionState; import org.apache.nifi.cluster.manager.NodeResponse; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.IllegalClusterResourceRequestException; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.AboutDTO; import org.apache.nifi.web.api.dto.BannerDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO; +import org.apache.nifi.web.api.dto.ClusterDTO; +import org.apache.nifi.web.api.dto.NodeDTO; import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.action.ActionDTO; @@ -67,6 +56,7 @@ import org.apache.nifi.web.api.dto.action.HistoryQueryDTO; import org.apache.nifi.web.api.dto.flow.FlowDTO; import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO; +import org.apache.nifi.web.api.dto.search.NodeSearchResultDTO; import org.apache.nifi.web.api.dto.search.SearchResultsDTO; import org.apache.nifi.web.api.dto.status.ConnectionStatusDTO; import org.apache.nifi.web.api.dto.status.ControllerStatusDTO; @@ -79,19 +69,19 @@ import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.entity.AboutEntity; import org.apache.nifi.web.api.entity.ActionEntity; -import org.apache.nifi.web.api.entity.AuthorityEntity; import org.apache.nifi.web.api.entity.BannerEntity; import org.apache.nifi.web.api.entity.BulletinBoardEntity; +import org.apache.nifi.web.api.entity.ClusterSearchResultsEntity; import org.apache.nifi.web.api.entity.ComponentHistoryEntity; import org.apache.nifi.web.api.entity.ConnectionStatusEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity; import org.apache.nifi.web.api.entity.ControllerServicesEntity; import org.apache.nifi.web.api.entity.ControllerStatusEntity; +import org.apache.nifi.web.api.entity.CurrentUserEntity; import org.apache.nifi.web.api.entity.Entity; import org.apache.nifi.web.api.entity.FlowConfigurationEntity; import org.apache.nifi.web.api.entity.HistoryEntity; -import org.apache.nifi.web.api.entity.IdentityEntity; import org.apache.nifi.web.api.entity.PortStatusEntity; import org.apache.nifi.web.api.entity.PrioritizerTypesEntity; import org.apache.nifi.web.api.entity.ProcessGroupEntity; @@ -106,19 +96,37 @@ import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import org.apache.nifi.web.api.entity.SearchResultsEntity; import org.apache.nifi.web.api.entity.StatusHistoryEntity; +import org.apache.nifi.web.api.entity.TemplateEntity; +import org.apache.nifi.web.api.entity.TemplatesEntity; import org.apache.nifi.web.api.request.BulletinBoardPatternParameter; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.DateTimeParameter; import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.LongParameter; -import com.sun.jersey.api.core.ResourceContext; -import com.wordnik.swagger.annotations.Api; -import com.wordnik.swagger.annotations.ApiOperation; -import com.wordnik.swagger.annotations.ApiParam; -import com.wordnik.swagger.annotations.ApiResponse; -import com.wordnik.swagger.annotations.ApiResponses; -import com.wordnik.swagger.annotations.Authorization; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * RESTful endpoint for managing a Flow. @@ -215,6 +223,21 @@ private void authorizeFlow(final RequestAction action) { } } + private boolean isAuthorized(final RequestAction action, final Resource resource) { + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(resource) + .identity(user.getIdentity()) + .anonymous(user.isAnonymous()) + .accessAttempt(false) + .action(action) + .build(); + + final AuthorizationResult result = authorizer.authorize(request); + return Result.Approved.equals(result.getResult()); + } + // ---- // flow // ---- @@ -299,12 +322,12 @@ public Response getFlowConfig() { @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) - @Path("identity") + @Path("current-user") @ApiOperation( value = "Retrieves the user identity of the user making the request", - response = IdentityEntity.class + response = CurrentUserEntity.class ) - public Response getIdentity() { + public Response getCurrentUser() { authorizeFlow(RequestAction.READ); @@ -315,59 +338,7 @@ public Response getIdentity() { } // create the response entity - IdentityEntity entity = new IdentityEntity(); - entity.setUserId(user.getIdentity()); - entity.setIdentity(user.getUserName()); - entity.setAnonymous(user.isAnonymous()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /**x - * Retrieves the user details, including the authorities, about the user making the request. - * - * @return A authoritiesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("authorities") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')") - @ApiOperation( - value = "Retrieves the user details, including the authorities, about the user making the request", - response = AuthorityEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN"), - @Authorization(value = "Proxy", type = "ROLE_PROXY"), - @Authorization(value = "NiFi", type = "ROLE_NIFI"), - @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response getAuthorities() { - // TODO - remove this method once authorities are completely removed - authorizeFlow(RequestAction.READ); - - // note that the cluster manager will handle this request directly - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - if (user == null) { - throw new WebApplicationException(new Throwable("Unable to access details for current user.")); - } - - // create the response entity - AuthorityEntity entity = new AuthorityEntity(); - entity.setUserId(user.getIdentity()); - entity.setAuthorities(new HashSet<>(Arrays.asList("ROLE_MONITOR", "ROLE_DFM", "ROLE_ADMIN", "ROLE_PROXY", "ROLE_NIFI", "ROLE_PROVENANCE"))); + final CurrentUserEntity entity = serviceFacade.getCurrentUser(); // generate the response return clusterContext(generateOkResponse(entity)).build(); @@ -1081,7 +1052,7 @@ public Response getAboutInfo() { // -------------- /** - * Retrieves all the of templates in this NiFi. + * Retrieves all the of bulletins in this NiFi. * * @param after Supporting querying for bulletins after a particular * bulletin id. @@ -1960,7 +1931,7 @@ public Response getConnectionStatusHistory( * formatted as 'MM/dd/yyyy HH:mm:ss'. This parameter is optional and must * be specified in the timezone of the server. The server's timezone can be * determined by inspecting the result of a status or history request. - * @param userName The user name of the user who's actions are being + * @param userIdentity The user name of the user who's actions are being * queried. This parameter is optional. * @param sourceId The id of the source being queried (usually a processor * id). This parameter is optional. @@ -2023,7 +1994,7 @@ public Response queryHistory( value = "Include actions performed by this user.", required = false ) - @QueryParam("userName") String userName, + @QueryParam("userIdentity") String userIdentity, @ApiParam( value = "Include actions on this component.", required = false @@ -2082,8 +2053,8 @@ public Response queryHistory( } // optionally set the user id - if (userName != null) { - query.setUserName(userName); + if (userIdentity != null) { + query.setUserIdentity(userIdentity); } // optionally set the processor id @@ -2210,6 +2181,138 @@ public Response getComponentHistory( return generateOkResponse(entity).build(); } + // --------- + // templates + // --------- + + /** + * Retrieves all the of templates in this NiFi. + * + * @return A templatesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("templates") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets all templates", + response = TemplatesEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getTemplates() { + + if (isReplicateRequest()) { + return replicate(HttpMethod.GET); + } + + // authorize access + authorizeFlow(RequestAction.READ); + + // get all the templates + final Set templates = serviceFacade.getTemplates(); + templateResource.populateRemainingTemplateEntitiesContent(templates); + + // create the response entity + final TemplatesEntity entity = new TemplatesEntity(); + entity.setTemplates(templates); + entity.setGenerated(new Date()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // -------------------- + // search cluster nodes + // -------------------- + + /** + * Searches the cluster for a node with a given address. + * + * @param value Search value that will be matched against a node's address + * @return Nodes that match the specified criteria + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("cluster/search-results") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Searches the cluster for a node with the specified address", + response = ClusterSearchResultsEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "DFM", type = "ROLE_DFM"), + @Authorization(value = "Admin", type = "ROLE_ADMIN") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response searchCluster( + @ApiParam( + value = "Node address to search for.", + required = true + ) + @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { + + authorizeFlow(RequestAction.READ); + + // ensure connected to the cluster + if (!isConnectedToCluster()) { + throw new IllegalClusterResourceRequestException("Only a node connected to a cluster can process the request."); + } + + final List nodeMatches = new ArrayList<>(); + + // get the nodes in the cluster + final ClusterDTO cluster = serviceFacade.getCluster(); + + // check each to see if it matches the search term + for (NodeDTO node : cluster.getNodes()) { + // ensure the node is connected + if (!NodeConnectionState.CONNECTED.name().equals(node.getStatus())) { + continue; + } + + // determine the current nodes address + final String address = node.getAddress() + ":" + node.getApiPort(); + + // count the node if there is no search or it matches the address + if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(address, value)) { + final NodeSearchResultDTO nodeMatch = new NodeSearchResultDTO(); + nodeMatch.setId(node.getNodeId()); + nodeMatch.setAddress(address); + nodeMatches.add(nodeMatch); + } + } + + // build the response + ClusterSearchResultsEntity results = new ClusterSearchResultsEntity(); + results.setNodeResults(nodeMatches); + + // generate an 200 - OK response + return noCache(Response.ok(results)).build(); + } + // setters public void setServiceFacade(NiFiServiceFacade serviceFacade) { this.serviceFacade = serviceFacade; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java index 48c241c2f12e..0a94fea0c505 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java @@ -16,36 +16,14 @@ */ package org.apache.nifi.web.api; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.stream.StreamSource; - +import com.sun.jersey.api.core.ResourceContext; +import com.sun.jersey.multipart.FormDataParam; +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.RequestAction; @@ -82,20 +60,38 @@ import org.apache.nifi.web.api.entity.RemoteProcessGroupEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupsEntity; import org.apache.nifi.web.api.entity.TemplateEntity; -import org.apache.nifi.web.api.entity.TemplatesEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.LongParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jersey.api.core.ResourceContext; -import com.sun.jersey.multipart.FormDataParam; -import com.wordnik.swagger.annotations.Api; -import com.wordnik.swagger.annotations.ApiOperation; -import com.wordnik.swagger.annotations.ApiParam; -import com.wordnik.swagger.annotations.ApiResponse; -import com.wordnik.swagger.annotations.ApiResponses; -import com.wordnik.swagger.annotations.Authorization; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** * RESTful endpoint for managing a Group. @@ -1847,62 +1843,6 @@ private void authorizeSnippetUsage(final AuthorizableLookup lookup, final String authorizeSnippet(snippet, authorizer, lookup, RequestAction.READ); } - /** - * Retrieves all the of templates in this NiFi. - * - * @return A templatesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("{id}/templates") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets all templates", - response = TemplatesEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response getTemplates( - @ApiParam( - value = "The process group id.", - required = true - ) - @PathParam("id") final String groupId) { - - if (isReplicateRequest()) { - return replicate(HttpMethod.GET); - } - - // authorize access - serviceFacade.authorizeAccess(lookup -> { - final Authorizable processGroup = lookup.getProcessGroup(groupId); - processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); - }); - - // get all the templates - final Set templates = templateResource.populateRemainingTemplatesContent(serviceFacade.getTemplates()); - - // create the response entity - final TemplatesEntity entity = new TemplatesEntity(); - entity.setTemplates(templates); - entity.setGenerated(new Date()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Creates a new template based off of the specified template. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceEventResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceEventResource.java new file mode 100644 index 000000000000..8c3a5d61851e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceEventResource.java @@ -0,0 +1,391 @@ +/* + * 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.web.api; + +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.annotations.Authorization; +import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator; +import org.apache.nifi.controller.repository.claim.ContentDirection; +import org.apache.nifi.stream.io.StreamUtils; +import org.apache.nifi.web.DownloadableContent; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.provenance.ProvenanceDTO; +import org.apache.nifi.web.api.dto.provenance.ProvenanceEventDTO; +import org.apache.nifi.web.api.dto.provenance.lineage.LineageDTO; +import org.apache.nifi.web.api.entity.ProvenanceEventEntity; +import org.apache.nifi.web.api.entity.SubmitReplayRequestEntity; +import org.apache.nifi.web.api.request.LongParameter; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; + + +/** + * RESTful endpoint for querying data provenance. + */ +@Path("/provenance-events") +@Api( + value = "/provenance-events", + description = "Endpoint for accessing data flow provenance." +) +public class ProvenanceEventResource extends ApplicationResource { + + private NiFiServiceFacade serviceFacade; + + /** + * Populates the uri for the specified provenance. + */ + private ProvenanceDTO populateRemainingProvenanceContent(ProvenanceDTO provenance) { + provenance.setUri(generateResourceUri("provenance", provenance.getId())); + return provenance; + } + + /** + * Populates the uri for the specified lineage. + */ + private LineageDTO populateRemainingLineageContent(LineageDTO lineage) { + lineage.setUri(generateResourceUri("provenance", "lineage", lineage.getId())); + return lineage; + } + + /** + * Gets the content for the input of the specified event. + * + * @param clusterNodeId The id of the node within the cluster this content is on. Required if clustered. + * @param id The id of the provenance event associated with this content. + * @return The content stream + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.WILDCARD) + @Path("{id}/content/input") + // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") + @ApiOperation( + value = "Gets the input content for a provenance event", + authorizations = { + @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getInputContent( + @ApiParam( + value = "The id of the node where the content exists if clustered.", + required = false + ) + @QueryParam("clusterNodeId") final String clusterNodeId, + @ApiParam( + value = "The provenance event id.", + required = true + ) + @PathParam("id") final LongParameter id) { + + // ensure proper input + if (id == null) { + throw new IllegalArgumentException("The event id must be specified."); + } + + // replicate if cluster manager + if (isReplicateRequest()) { + // determine where this request should be sent + if (clusterNodeId == null) { + throw new IllegalArgumentException("The id of the node in the cluster is required."); + } else { + return replicate(HttpMethod.GET, clusterNodeId); + } + } + + // get the uri of the request + final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "input"); + + // get an input stream to the content + final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.INPUT); + + // generate a streaming response + final StreamingOutput response = new StreamingOutput() { + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + try (InputStream is = content.getContent()) { + // stream the content to the response + StreamUtils.copy(is, output); + + // flush the response + output.flush(); + } + } + }; + + // use the appropriate content type + String contentType = content.getType(); + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + + return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); + } + + /** + * Gets the content for the output of the specified event. + * + * @param clusterNodeId The id of the node within the cluster this content is on. Required if clustered. + * @param id The id of the provenance event associated with this content. + * @return The content stream + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.WILDCARD) + @Path("{id}/content/output") + // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") + @ApiOperation( + value = "Gets the output content for a provenance event", + authorizations = { + @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getOutputContent( + @ApiParam( + value = "The id of the node where the content exists if clustered.", + required = false + ) + @QueryParam("clusterNodeId") final String clusterNodeId, + @ApiParam( + value = "The provenance event id.", + required = true + ) + @PathParam("id") final LongParameter id) { + + // ensure proper input + if (id == null) { + throw new IllegalArgumentException("The event id must be specified."); + } + + // replicate if cluster manager + if (isReplicateRequest()) { + // determine where this request should be sent + if (clusterNodeId == null) { + throw new IllegalArgumentException("The id of the node in the cluster is required."); + } else { + return replicate(HttpMethod.GET, clusterNodeId); + } + } + + // get the uri of the request + final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "output"); + + // get an input stream to the content + final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.OUTPUT); + + // generate a streaming response + final StreamingOutput response = new StreamingOutput() { + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + try (InputStream is = content.getContent()) { + // stream the content to the response + StreamUtils.copy(is, output); + + // flush the response + output.flush(); + } + } + }; + + // use the appropriate content type + String contentType = content.getType(); + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + + return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); + } + + /** + * Gets the details for a provenance event. + * + * @param id The id of the event + * @param clusterNodeId The id of node in the cluster that the event/flowfile originated from. This is only required when clustered. + * @return A provenanceEventEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}") + // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") + @ApiOperation( + value = "Gets a provenance event", + response = ProvenanceEventEntity.class, + authorizations = { + @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response getProvenanceEvent( + @ApiParam( + value = "The id of the node where this event exists if clustered.", + required = false + ) + @QueryParam("clusterNodeId") final String clusterNodeId, + @ApiParam( + value = "The provenence event id.", + required = true + ) + @PathParam("id") final LongParameter id) { + + // ensure the id is specified + if (id == null) { + throw new IllegalArgumentException("Provenance event id must be specified."); + } + + // replicate if cluster manager + if (isReplicateRequest()) { + // since we're cluster we must specify the cluster node identifier + if (clusterNodeId == null) { + throw new IllegalArgumentException("The cluster node identifier must be specified."); + } + + return replicate(HttpMethod.GET, clusterNodeId); + } + + // get the provenance event + final ProvenanceEventDTO event = serviceFacade.getProvenanceEvent(id.getLong()); + + // create a response entity + final ProvenanceEventEntity entity = new ProvenanceEventEntity(); + entity.setProvenanceEvent(event); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Creates a new replay request for the content associated with the specified provenance event id. + * + * @param httpServletRequest request + * @param replayRequestEntity The replay request + * @return A provenanceEventEntity + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("replays") + // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE') and hasRole('ROLE_DFM')") + @ApiOperation( + value = "Replays content from a provenance event", + response = ProvenanceEventEntity.class, + authorizations = { + @Authorization(value = "Provenance and Data Flow Manager", type = "ROLE_PROVENANCE and ROLE_DFM") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + } + ) + public Response submitReplay( + @Context final HttpServletRequest httpServletRequest, + @ApiParam( + value = "The replay request.", + required = true + ) final SubmitReplayRequestEntity replayRequestEntity) { + + // ensure the event id is specified + if (replayRequestEntity == null || replayRequestEntity.getEventId() == null) { + throw new IllegalArgumentException("The id of the event must be specified."); + } + + // replicate if cluster manager + if (isReplicateRequest()) { + // determine where this request should be sent + if (replayRequestEntity.getClusterNodeId() == null) { + throw new IllegalArgumentException("The id of the node in the cluster is required."); + } else { + return replicate(HttpMethod.POST, replayRequestEntity, replayRequestEntity.getClusterNodeId()); + } + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(RequestReplicator.REQUEST_VALIDATION_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // submit the provenance replay request + final ProvenanceEventDTO event = serviceFacade.submitReplay(replayRequestEntity.getEventId()); + + // create a response entity + final ProvenanceEventEntity entity = new ProvenanceEventEntity(); + entity.setProvenanceEvent(event); + + // generate the response + URI uri = URI.create(generateResourceUri("provenance-events", event.getId())); + return clusterContext(generateCreatedResponse(uri, entity)).build(); + } + + // setters + + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java index f4501f5db217..70339eaebb6a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java @@ -16,29 +16,12 @@ */ package org.apache.nifi.web.api; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; - +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationRequest; @@ -50,28 +33,31 @@ import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator; -import org.apache.nifi.controller.repository.claim.ContentDirection; -import org.apache.nifi.stream.io.StreamUtils; -import org.apache.nifi.web.DownloadableContent; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.api.dto.provenance.ProvenanceDTO; -import org.apache.nifi.web.api.dto.provenance.ProvenanceEventDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceOptionsDTO; import org.apache.nifi.web.api.dto.provenance.lineage.LineageDTO; import org.apache.nifi.web.api.dto.provenance.lineage.LineageRequestDTO; import org.apache.nifi.web.api.entity.LineageEntity; import org.apache.nifi.web.api.entity.ProvenanceEntity; -import org.apache.nifi.web.api.entity.ProvenanceEventEntity; import org.apache.nifi.web.api.entity.ProvenanceOptionsEntity; -import org.apache.nifi.web.api.entity.SubmitReplayRequestEntity; -import org.apache.nifi.web.api.request.LongParameter; -import com.wordnik.swagger.annotations.Api; -import com.wordnik.swagger.annotations.ApiOperation; -import com.wordnik.swagger.annotations.ApiParam; -import com.wordnik.swagger.annotations.ApiResponse; -import com.wordnik.swagger.annotations.ApiResponses; -import com.wordnik.swagger.annotations.Authorization; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; /** @@ -165,246 +151,6 @@ public Response getSearchOptions() { return clusterContext(noCache(Response.ok(entity))).build(); } - /** - * Creates a new replay request for the content associated with the specified provenance event id. - * - * @param httpServletRequest request - * @param replayRequestEntity The replay request - * @return A provenanceEventEntity - */ - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @Path("replays") - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE') and hasRole('ROLE_DFM')") - @ApiOperation( - value = "Replays content from a provenance event", - response = ProvenanceEventEntity.class, - authorizations = { - @Authorization(value = "Provenance and Data Flow Manager", type = "ROLE_PROVENANCE and ROLE_DFM") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response submitReplay( - @Context final HttpServletRequest httpServletRequest, - @ApiParam( - value = "The replay request.", - required = true - ) final SubmitReplayRequestEntity replayRequestEntity) { - - authorizeProvenanceRequest(); - - // ensure the event id is specified - if (replayRequestEntity == null || replayRequestEntity.getEventId() == null) { - throw new IllegalArgumentException("The id of the event must be specified."); - } - - // replicate if cluster manager - if (isReplicateRequest()) { - // determine where this request should be sent - if (replayRequestEntity.getClusterNodeId() == null) { - throw new IllegalArgumentException("The id of the node in the cluster is required."); - } else { - return replicate(HttpMethod.POST, replayRequestEntity, replayRequestEntity.getClusterNodeId()); - } - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(RequestReplicator.REQUEST_VALIDATION_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // submit the provenance replay request - final ProvenanceEventDTO event = serviceFacade.submitReplay(replayRequestEntity.getEventId()); - - // create a response entity - final ProvenanceEventEntity entity = new ProvenanceEventEntity(); - entity.setProvenanceEvent(event); - - // generate the response - URI uri = URI.create(generateResourceUri("provenance", "events", event.getId())); - return clusterContext(generateCreatedResponse(uri, entity)).build(); - } - - /** - * Gets the content for the input of the specified event. - * - * @param clusterNodeId The id of the node within the cluster this content is on. Required if clustered. - * @param id The id of the provenance event associated with this content. - * @return The content stream - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.WILDCARD) - @Path("events/{id}/content/input") - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") - @ApiOperation( - value = "Gets the input content for a provenance event", - authorizations = { - @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response getInputContent( - @ApiParam( - value = "The id of the node where the content exists if clustered.", - required = false - ) - @QueryParam("clusterNodeId") final String clusterNodeId, - @ApiParam( - value = "The provenance event id.", - required = true - ) - @PathParam("id") final LongParameter id) { - - authorizeProvenanceRequest(); - - // ensure proper input - if (id == null) { - throw new IllegalArgumentException("The event id must be specified."); - } - - // replicate if cluster manager - if (isReplicateRequest()) { - // determine where this request should be sent - if (clusterNodeId == null) { - throw new IllegalArgumentException("The id of the node in the cluster is required."); - } else { - return replicate(HttpMethod.GET, clusterNodeId); - } - } - - // get the uri of the request - final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "input"); - - // get an input stream to the content - final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.INPUT); - - // generate a streaming response - final StreamingOutput response = new StreamingOutput() { - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - try (InputStream is = content.getContent()) { - // stream the content to the response - StreamUtils.copy(is, output); - - // flush the response - output.flush(); - } - } - }; - - // use the appropriate content type - String contentType = content.getType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_OCTET_STREAM; - } - - return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); - } - - /** - * Gets the content for the output of the specified event. - * - * @param clusterNodeId The id of the node within the cluster this content is on. Required if clustered. - * @param id The id of the provenance event associated with this content. - * @return The content stream - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.WILDCARD) - @Path("events/{id}/content/output") - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") - @ApiOperation( - value = "Gets the output content for a provenance event", - authorizations = { - @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response getOutputContent( - @ApiParam( - value = "The id of the node where the content exists if clustered.", - required = false - ) - @QueryParam("clusterNodeId") final String clusterNodeId, - @ApiParam( - value = "The provenance event id.", - required = true - ) - @PathParam("id") final LongParameter id) { - - authorizeProvenanceRequest(); - - // ensure proper input - if (id == null) { - throw new IllegalArgumentException("The event id must be specified."); - } - - // replicate if cluster manager - if (isReplicateRequest()) { - // determine where this request should be sent - if (clusterNodeId == null) { - throw new IllegalArgumentException("The id of the node in the cluster is required."); - } else { - return replicate(HttpMethod.GET, clusterNodeId); - } - } - - // get the uri of the request - final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "output"); - - // get an input stream to the content - final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.OUTPUT); - - // generate a streaming response - final StreamingOutput response = new StreamingOutput() { - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - try (InputStream is = content.getContent()) { - // stream the content to the response - StreamUtils.copy(is, output); - - // flush the response - output.flush(); - } - } - }; - - // use the appropriate content type - String contentType = content.getType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_OCTET_STREAM; - } - - return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); - } - /** * Creates provenance using the specified query criteria. * @@ -634,74 +380,6 @@ public Response deleteProvenance( return clusterContext(generateOkResponse(entity)).build(); } - /** - * Gets the details for a provenance event. - * - * @param id The id of the event - * @param clusterNodeId The id of node in the cluster that the event/flowfile originated from. This is only required when clustered. - * @return A provenanceEventEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("events/{id}") - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") - @ApiOperation( - value = "Gets a provenance event", - response = ProvenanceEventEntity.class, - authorizations = { - @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") - } - ) - public Response getProvenanceEvent( - @ApiParam( - value = "The id of the node where this event exists if clustered.", - required = false - ) - @QueryParam("clusterNodeId") final String clusterNodeId, - @ApiParam( - value = "The provenence event id.", - required = true - ) - @PathParam("id") final LongParameter id) { - - authorizeProvenanceRequest(); - - // ensure the id is specified - if (id == null) { - throw new IllegalArgumentException("Provenance event id must be specified."); - } - - // replicate if cluster manager - if (isReplicateRequest()) { - // since we're cluster we must specify the cluster node identifier - if (clusterNodeId == null) { - throw new IllegalArgumentException("The cluster node identifier must be specified."); - } - - return replicate(HttpMethod.GET, clusterNodeId); - } - - // get the provenance event - final ProvenanceEventDTO event = serviceFacade.getProvenanceEvent(id.getLong()); - - // create a response entity - final ProvenanceEventEntity entity = new ProvenanceEventEntity(); - entity.setProvenanceEvent(event); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Submits a lineage request based on an event or a flowfile uuid. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java index 699481519ea4..dcff518b0850 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java @@ -16,7 +16,20 @@ */ package org.apache.nifi.web.api; -import java.util.Set; +import com.wordnik.swagger.annotations.Api; +import com.wordnik.swagger.annotations.ApiOperation; +import com.wordnik.swagger.annotations.ApiParam; +import com.wordnik.swagger.annotations.ApiResponse; +import com.wordnik.swagger.annotations.ApiResponses; +import com.wordnik.swagger.annotations.Authorization; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.TemplateDTO; +import org.apache.nifi.web.api.entity.TemplateEntity; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -29,22 +42,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - -import org.apache.commons.lang3.StringUtils; -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.NiFiUserUtils; -import org.apache.nifi.web.NiFiServiceFacade; -import org.apache.nifi.web.api.dto.TemplateDTO; -import org.apache.nifi.web.api.entity.TemplateEntity; - -import com.wordnik.swagger.annotations.Api; -import com.wordnik.swagger.annotations.ApiOperation; -import com.wordnik.swagger.annotations.ApiParam; -import com.wordnik.swagger.annotations.ApiResponse; -import com.wordnik.swagger.annotations.ApiResponses; -import com.wordnik.swagger.annotations.Authorization; +import java.util.Set; /** * RESTful endpoint for managing a Template. @@ -59,6 +57,34 @@ public class TemplateResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; private Authorizer authorizer; + /** + * Populate the uri's for the specified templates. + * + * @param templateEntities templates + * @return templates + */ + public Set populateRemainingTemplateEntitiesContent(Set templateEntities) { + for (TemplateEntity templateEntity : templateEntities) { + if (templateEntity.getTemplate() != null) { + populateRemainingTemplateContent(templateEntity.getTemplate()); + } + } + return templateEntities; + } + + /** + * Populate the uri's for the specified templates. + * + * @param templateEntity templates + * @return templates + */ + public TemplateEntity populateRemainingTemplateEntityContent(TemplateEntity templateEntity) { + if (templateEntity.getTemplate() != null) { + populateRemainingTemplateContent(templateEntity.getTemplate()); + } + return templateEntity; + } + /** * Populates the uri for the specified templates. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index 10a28c7eda32..cf1bca49bdee 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -220,7 +220,6 @@ public ActionDTO createActionDto(final Action action) { actionDto.setSourceType(action.getSourceType().name()); actionDto.setTimestamp(action.getTimestamp()); actionDto.setUserDn(action.getUserIdentity()); - actionDto.setUserName(action.getUserName()); actionDto.setOperation(action.getOperation().name()); actionDto.setActionDetails(createActionDetailsDto(action.getActionDetails())); actionDto.setComponentDetails(createComponentDetailsDto(action.getComponentDetails())); @@ -305,15 +304,6 @@ public HistoryDTO createHistoryDto(final History history) { final HistoryDTO historyDto = new HistoryDTO(); historyDto.setTotal(history.getTotal()); historyDto.setLastRefreshed(history.getLastRefreshed()); - - if (history.getActions() != null) { - final List actionDtos = new ArrayList<>(); - for (final Action action : history.getActions()) { - actionDtos.add(createActionDto(action)); - } - historyDto.setActions(actionDtos); - } - return historyDto; } @@ -830,6 +820,7 @@ public TemplateDTO createTemplateDTO(final Template template) { final TemplateDTO copy = new TemplateDTO(); copy.setId(original.getId()); + copy.setGroupId(template.getProcessGroup().getIdentifier()); copy.setName(original.getName()); copy.setDescription(original.getDescription()); copy.setTimestamp(original.getTimestamp()); 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 b72d72c166f1..2c6d94837d32 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,6 +19,9 @@ 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.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; @@ -1138,22 +1141,8 @@ public DownloadableContent getContent(final Long eventId, final String uri, fina attributes = event.getAttributes(); } - // calculate the dn chain - final List dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user); - dnChain.forEach(identity -> { - 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(); - } - }; - - eventAuthorizable.authorize(authorizer, RequestAction.READ, chainUser); - }); + // authorize the event + authorizeEvent(event.getComponentId(), attributes); // get the filename and fall back to the identifier (should never happen) String filename = attributes.get(CoreAttributes.FILENAME.key()); @@ -1197,6 +1186,9 @@ public ProvenanceEventDTO submitReplay(final Long eventId) { // replay the flow file final ProvenanceEventRecord event = flowController.replayFlowFile(originalEvent, user); + // authorize the replay + authorizeReplay(event.getComponentId(), event.getAttributes(), event.getSourceQueueIdentifier()); + // convert the event record return createProvenanceEventDto(event); } catch (final IOException ioe) { @@ -1204,6 +1196,112 @@ public ProvenanceEventDTO submitReplay(final Long eventId) { } } + /** + * Authorizes access to a provenance event generated by the specified component and containing the specified eventAttributes. + * + * @param componentId component id + * @param eventAttributes event attributes + */ + private AuthorizationResult checkAuthorizationForEvent(final String componentId, final Map eventAttributes) { + AuthorizationResult result = null; + + // calculate the dn chain + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final List dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user); + for (final String identity : dnChain) { + final Authorizable eventAuthorizable = flowController.createProvenanceAuthorizable(componentId); + final NiFiUser chainUser = new StandardNiFiUser(identity) { + @Override + public boolean isAnonymous() { + // allow current user to drive anonymous flag as anonymous users are never chained... supports single user case + return user.isAnonymous(); + } + }; + + result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, chainUser, eventAttributes); + if (!Result.Approved.equals(result.getResult())) { + break; + } + } + + if (result == null) { + result = AuthorizationResult.denied(); + } + + return result; + } + + /** + * Authorizes access to a provenance event generated by the specified component and containing the specified eventAttributes. + * + * @param componentId component id + * @param eventAttributes event attributes + */ + private void authorizeEvent(final String componentId, final Map eventAttributes) { + // calculate the dn chain + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final List dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user); + for (final String identity : dnChain) { + final Authorizable eventAuthorizable = flowController.createProvenanceAuthorizable(componentId); + final NiFiUser chainUser = new StandardNiFiUser(identity) { + @Override + public boolean isAnonymous() { + // allow current user to drive anonymous flag as anonymous users are never chained... supports single user case + return user.isAnonymous(); + } + }; + + eventAuthorizable.authorize(authorizer, RequestAction.READ, chainUser, eventAttributes); + } + } + + /** + * Authorizes access to replay a specified provenance event. + * + * @param componentId component id + * @param eventAttributes event attributes + * @param connectionId connection id + */ + private AuthorizationResult checkAuthorizationForReplay(final String componentId, final Map eventAttributes, final String connectionId) { + // 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 (connectionId == null) { + return AuthorizationResult.denied(); + } + + final AuthorizationResult result = checkAuthorizationForEvent(componentId, eventAttributes); + if (!Result.Approved.equals(result.getResult())) { + return result; + } + + // authorize write permissions for the queue + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final ProcessGroup rootGroup = flowController.getGroup(flowController.getRootGroupId()); + final Connection connection = rootGroup.findConnection(connectionId); + return connection.checkAuthorization(authorizer, RequestAction.WRITE, user); + } + + /** + * Authorizes access to replay a specified provenance event. + * + * @param componentId component id + * @param eventAttributes event attributes + * @param connectionId connection id + */ + private void authorizeReplay(final String componentId, final Map eventAttributes, final String connectionId) { + // 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 (connectionId == null) { + throw new AccessDeniedException("The connection id is unknown."); + } + + authorizeEvent(componentId, eventAttributes); + + // authorize write permissions for the queue + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + final ProcessGroup rootGroup = flowController.getGroup(flowController.getRootGroupId()); + final Connection connection = rootGroup.findConnection(connectionId); + connection.authorize(authorizer, RequestAction.WRITE, user); + } + /** * Get the provenance event with the specified event id. * @@ -1217,6 +1315,10 @@ public ProvenanceEventDTO getProvenanceEvent(final Long eventId) { throw new ResourceNotFoundException("Unable to find the specified event."); } + // get the flowfile attributes and authorize the event + final Map attributes = event.getAttributes(); + authorizeEvent(event.getComponentId(), attributes); + // convert the event return createProvenanceEventDto(event); } catch (final IOException ioe) { @@ -1308,9 +1410,13 @@ public int compare(AttributeDTO a1, AttributeDTO a2) { dto.setInputContentClaimFileSize(FormatUtils.formatDataSize(event.getPreviousFileSize())); } + // determine if authorized for event replay + final AuthorizationResult replayAuthorized = checkAuthorizationForReplay(event.getComponentId(), event.getAttributes(), event.getSourceQueueIdentifier()); + // replay - dto.setReplayAvailable(contentAvailability.isReplayable()); - dto.setReplayExplanation(contentAvailability.getReasonNotReplayable()); + dto.setReplayAvailable(contentAvailability.isReplayable() && Result.Approved.equals(replayAuthorized.getResult())); + dto.setReplayExplanation(contentAvailability.isReplayable() + && !Result.Approved.equals(replayAuthorized.getResult()) ? replayAuthorized.getExplanation() : contentAvailability.getReasonNotReplayable()); dto.setSourceConnectionIdentifier(event.getSourceQueueIdentifier()); // sets the component details if it can find the component still in the flow @@ -1366,56 +1472,72 @@ public SearchResultsDTO search(final String search) { } private void search(final SearchResultsDTO results, final String search, final ProcessGroup group) { - final ComponentSearchResultDTO groupMatch = search(search, group); - if (groupMatch != null) { - results.getProcessGroupResults().add(groupMatch); + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + + if (group.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO groupMatch = search(search, group); + if (groupMatch != null) { + results.getProcessGroupResults().add(groupMatch); + } } for (final ProcessorNode procNode : group.getProcessors()) { - final ComponentSearchResultDTO match = search(search, procNode); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getProcessorResults().add(match); + if (procNode.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, procNode); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getProcessorResults().add(match); + } } } for (final Connection connection : group.getConnections()) { - final ComponentSearchResultDTO match = search(search, connection); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getConnectionResults().add(match); + if (connection.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, connection); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getConnectionResults().add(match); + } } } for (final RemoteProcessGroup remoteGroup : group.getRemoteProcessGroups()) { - final ComponentSearchResultDTO match = search(search, remoteGroup); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getRemoteProcessGroupResults().add(match); + if (remoteGroup.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, remoteGroup); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getRemoteProcessGroupResults().add(match); + } } } for (final Port port : group.getInputPorts()) { - final ComponentSearchResultDTO match = search(search, port); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getInputPortResults().add(match); + if (port.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, port); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getInputPortResults().add(match); + } } } for (final Port port : group.getOutputPorts()) { - final ComponentSearchResultDTO match = search(search, port); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getOutputPortResults().add(match); + if (port.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, port); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getOutputPortResults().add(match); + } } } for (final Funnel funnel : group.getFunnels()) { - final ComponentSearchResultDTO match = search(search, funnel); - if (match != null) { - match.setGroupId(group.getIdentifier()); - results.getFunnelResults().add(match); + if (funnel.isAuthorized(authorizer, RequestAction.READ, user)) { + final ComponentSearchResultDTO match = search(search, funnel); + if (match != null) { + match.setGroupId(group.getIdentifier()); + results.getFunnelResults().add(match); + } } } 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 1a2669bb112c..1ac13ce0b267 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 @@ -604,7 +604,7 @@ public DownloadableContent getContent(String id, String flowFileUuid, String req .accessAttempt(false) .action(RequestAction.WRITE) .resource(connection.getResource()) - .eventAttributes(attributes) + .resourceContext(attributes) .build(); // perform the authorization diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index 8faedfb5358f..a1a63a626aef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -327,6 +327,12 @@ + + + + + + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/revision/NaiveRevisionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/revision/NaiveRevisionManager.java index 2171ea89fbee..ecc725ba63bc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/revision/NaiveRevisionManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/revision/NaiveRevisionManager.java @@ -217,7 +217,7 @@ public T deleteRevision(final RevisionClaim claim, final NiFiUser user, fina for (final Revision revision : revisionList) { final RevisionLock revisionLock = getRevisionLock(revision); - revisionLock.unlock(revision, revision, user.getUserName()); + revisionLock.unlock(revision, revision, user.getIdentity()); logger.debug("Relinquished lock for {}", revision); } @@ -300,7 +300,7 @@ public RevisionUpdate updateRevision(final RevisionClaim originalClaim, f for (final Revision revision : revisionList) { final Revision updatedRevision = updatedRevisions.get(revision); - getRevisionLock(revision).unlock(revision, updatedRevision, user.getUserName()); + getRevisionLock(revision).unlock(revision, updatedRevision, user.getIdentity()); if (updatedRevision.getVersion() != revision.getVersion()) { logger.debug("Unlocked Revision {} and updated associated Version to {}", revision, updatedRevision.getVersion()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java deleted file mode 100644 index 8451c7c6867f..000000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.security.node; - -import java.io.IOException; -import java.io.Serializable; -import java.security.cert.X509Certificate; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; - -import org.apache.nifi.authorization.user.NiFiUserDetails; -import org.apache.nifi.controller.FlowController; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.authentication.AuthenticationResponse; -import org.apache.nifi.authorization.user.NiFiUser; -import org.apache.nifi.util.NiFiProperties; -import org.apache.nifi.web.security.token.NiFiAuthenticationToken; -import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509IdentityProvider; -import org.apache.nifi.web.util.WebUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.context.support.WebApplicationContextUtils; -import org.springframework.web.filter.GenericFilterBean; - -/** - * Custom filter to extract a user's authorities from the request where the user was authenticated by the cluster manager and populate the threadlocal with the authorized user. If the request contains - * the appropriate header with authorities and the application instance is a node connected to the cluster, then the authentication/authorization steps remaining in the filter chain are skipped. - * - * Checking if the application instance is a connected node is important because it prevents external clients from faking the request headers and bypassing the authentication processing chain. - */ -public class NodeAuthorizedUserFilter extends GenericFilterBean { - - private static final Logger LOGGER = LoggerFactory.getLogger(NodeAuthorizedUserFilter.class); - - public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails"; - - private NiFiProperties properties; - private X509CertificateExtractor certificateExtractor; - private X509IdentityProvider certificateIdentityProvider; - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - final HttpServletRequest httpServletRequest = (HttpServletRequest) request; - - // get the proxied user's authorities - final String hexEncodedUserDetails = httpServletRequest.getHeader(PROXY_USER_DETAILS); - - // check if the request has the necessary header information and this instance is configured as a node - if (StringUtils.isNotBlank(hexEncodedUserDetails) && properties.isNode()) { - - // get the flow controller from the Spring context - final ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); - final FlowController flowController = ctx.getBean("flowController", FlowController.class); - - // check that we are connected to the cluster - if (flowController.getNodeId() != null) { - try { - // attempt to extract the client certificate - final X509Certificate[] certificate = certificateExtractor.extractClientCertificate(httpServletRequest); - if (certificate != null) { - // authenticate the certificate - final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificate); - - // only consider the pre-authorized user when the request came directly from the NCM according to the DN in the certificate - final String clusterManagerIdentity = flowController.getClusterManagerDN(); - if (clusterManagerIdentity != null && clusterManagerIdentity.equals(authenticationResponse.getIdentity())) { - // deserialize hex encoded object - final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails); - - // if we have a valid object, set the authentication token and bypass the remaining authentication processing chain - if (userDetailsObj instanceof NiFiUserDetails) { - final NiFiUserDetails userDetails = (NiFiUserDetails) userDetailsObj; - final NiFiUser user = userDetails.getNiFiUser(); - - // log the request attempt - response details will be logged later - logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getIdentity(), httpServletRequest.getMethod(), - httpServletRequest.getRequestURL().toString(), request.getRemoteAddr())); - - // create the authorized nifi token - final NiFiAuthenticationToken token = new NiFiAuthenticationToken(userDetails); - SecurityContextHolder.getContext().setAuthentication(token); - } - } - } - } catch (final ClassNotFoundException cnfe) { - LOGGER.warn("Classpath issue detected because failed to deserialize authorized user in request header due to: " + cnfe, cnfe); - } catch (final IllegalArgumentException iae) { - // unable to authenticate a serialized user from the incoming request - } - } - } - - chain.doFilter(request, response); - } - - public void setProperties(NiFiProperties properties) { - this.properties = properties; - } - - public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) { - this.certificateIdentityProvider = certificateIdentityProvider; - } - - public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { - this.certificateExtractor = certificateExtractor; - } - -} 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 b72d5e18c4d7..92e690c2909b 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 @@ -65,7 +65,7 @@ public Authentication authenticate(Authentication authentication) throws Authent } if (StringUtils.isBlank(request.getProxiedEntitiesChain())) { - return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(authenticationResponse.getIdentity(), authenticationResponse.getUsername(), null))); + return new NiFiAuthenticationToken(new NiFiUserDetails(new StandardNiFiUser(authenticationResponse.getIdentity()))); } else { // build the entire proxy chain if applicable - final List proxyChain = new ArrayList<>(ProxiedEntitiesUtils.tokenizeProxiedEntitiesChain(request.getProxiedEntitiesChain())); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index 77ee3f327e50..39fdffd3da15 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -49,6 +49,7 @@ + @@ -103,6 +104,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp index 91409e7da225..0803d226ad4b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp @@ -35,7 +35,6 @@ - diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp index 0f3a434a4870..050b25e63e62 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp @@ -97,7 +97,8 @@ + ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.counters.shell.launch();" + ng-class="{disabled: !appCtrl.nf.Common.canAccessCounters()}"> Counters @@ -109,24 +110,26 @@ + ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.dataProvenance.shell.launch();" + ng-class="{disabled: !appCtrl.nf.Common.canAccessProvenance()}"> Data Provenance + ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.controllerSettings.shell.launch();" + ng-class="{disabled: !appCtrl.nf.Common.canAccessController()}"> Controller Settings - + ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.cluster.shell.launch();" + ng-class="{disabled: !appCtrl.nf.Common.canAccessController()}"> Cluster @@ -136,10 +139,10 @@ Flow Configuration History - + + ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.users.shell.launch();" + ng-class="{disabled: !appCtrl.nf.Common.canAccessTenants()}"> Users diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp index 59d2a4535d61..b2b260662321 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp @@ -127,7 +127,13 @@
+
+
+
 
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/upload-template-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/upload-template-dialog.jsp new file mode 100644 index 000000000000..3c63209011b1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/upload-template-dialog.jsp @@ -0,0 +1,35 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp index 8ec0a59b23f0..00b4e5e3824f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp @@ -29,25 +29,6 @@
-
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css index 72a34fab2024..caf4003fe845 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/header.css @@ -104,7 +104,7 @@ md-toolbar.md-small .md-toolbar-tools { margin-bottom: 3px; } -#global-menu-content a{ +#global-menu-content a { height: 28px; padding: 6px 16px; cursor: pointer; @@ -112,6 +112,15 @@ md-toolbar.md-small .md-toolbar-tools { padding-right: 10px; } +#global-menu-content a.disabled { + color: #a8a8a8; + cursor: not-allowed; +} + +#global-menu-content a.disabled i { + color: #bebebe; +} + #global-menu-content a:hover{ background-color:#C7D2D7; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css index f2a8d42f79ce..a8097605416e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css @@ -614,4 +614,79 @@ div.secondary-button:hover { md-progress-linear > div { background-color: #eaeef0 !important; +} + +/* new template */ + +#new-template-description { + height: 210px; +} + +/* upload */ + +#select-template-container { +} + +#template-browse-container { + display: block; + height: 20px; +} + +#select-template-label { + font-weight: bold; + font-size: 14px; + line-height: 28px; +} + +#select-template-button { + position: absolute; + top: 66px; + left: 135px; + cursor: pointer; + overflow: hidden; +} + +#template-file-field { + height: 28px; + display: inline; + position: absolute; + top: 0; + left: 0; + opacity: 0; + filter: alpha(opacity = 0); + z-index: 3000; + cursor: pointer; +} + +#submit-template-container { + margin-top: 20px; +} + +#selected-template-name { + font-weight: bold; + font-family: Roboto; + font-size: 13px; +} + +#upload-template-status { + margin-top: 10px; + font-weight: bold; + font-family: Roboto; + font-size: 13px; + color: #ba554a; +} + +#upload-template-container button { + float: right; + font-size: 16px; + line-height: 14px; + margin-left: 1px; +} + +#template-file-upload { + position: absolute; + top: 0; + left: 0; + height: 22px; + width: 300px; } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/templates.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/templates.css index ed3d5e761881..762fdf581ad5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/templates.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/templates.css @@ -90,96 +90,6 @@ input.templates-filter-list { float: left; } -#new-template-description { - height: 210px; -} - -/* upload */ - -#select-template-container { -} - -#template-browse-container { - display: block; - height: 20px; -} - -#template-file-field { - height: 22px; - display: inline; - position: absolute; - top: -1px; - right: -1px; - float: right; - opacity: 0; - filter: alpha(opacity = 0); - z-index: 3000; - cursor: pointer; -} - -.import-status-error { - color: #ff0000; -} - -.import-status { - color: #888888; -} - -#upload-template-container { - float: right; - position: relative; - top: -28px; - overflow: hidden; -} - -#submit-template-container { - display: none; -} - -#selected-template-name { - float: right; - font-weight: bold; - margin-right: 20px; - margin-top: 6px; - font-family: Roboto; - font-size: 13px; - color: #728e9b; -} - -#upload-template-status { - float: right; - font-weight: bold; - margin-right: 20px; - margin-top: 6px; - line-height: normal; - font-family: Roboto; - font-size: 13px; - color: #728e9b; -} - -#upload-template-container button { - float: right; - font-size: 16px; - line-height: 14px; - margin-left: 1px; -} - -#template-upload-form-container { - display: none; -} - -#upload-template-button { - margin-right: 0; -} - -#template-file-upload { - position: absolute; - top: 0; - left: 0; - height: 22px; - width: 300px; -} - /* templates table */ #templates-table { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js index 0823f56babf3..0d00e278a934 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-global-menu-controller.js @@ -62,7 +62,9 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the counters shell. */ launch: function () { - nf.Shell.showPage('counters'); + if (nf.Common.canAccessCounters()) { + nf.Shell.showPage('counters'); + } } } }; @@ -91,15 +93,6 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { */ this.dataProvenance = { - /** - * Determines if the data provenance menu item is enabled. - * - * @returns {*|boolean} - */ - enabled: function () { - return nf.Common.canAccessProvenance(); - }, - /** * The data provenance menu item's shell controller. */ @@ -109,7 +102,9 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the data provenance shell. */ launch: function () { - nf.Shell.showPage('provenance'); + if (nf.Common.canAccessProvenance()) { + nf.Shell.showPage('provenance'); + } } } }; @@ -128,14 +123,16 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the settings shell. */ launch: function () { - nf.Settings.showSettings().done(function() { - $('#settings-refresh-container').width($('#shell').width()); - - // add a shell:resize listener - $('#shell').on('shell:resize', function () { + if (nf.Common.canAccessController()) { + nf.Settings.showSettings().done(function() { $('#settings-refresh-container').width($('#shell').width()); + + // add a shell:resize listener + $('#shell').on('shell:resize', function () { + $('#settings-refresh-container').width($('#shell').width()); + }); }); - }); + } } } }; @@ -150,7 +147,7 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * * @returns {*|boolean} */ - enabled: function () { + visible: function () { return nf.Canvas.isClustered(); }, @@ -163,7 +160,9 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the cluster shell. */ launch: function () { - nf.Shell.showPage('cluster'); + if (nf.Common.canAccessController()) { + nf.Shell.showPage('cluster'); + } } } }; @@ -192,15 +191,6 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { */ this.users = { - /** - * Determines if the users menu item is enabled. - * - * @returns {*|boolean} - */ - enabled: function () { - return nf.Common.isAdmin(); - }, - /** * The users menu item's shell controller. */ @@ -210,7 +200,9 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) { * Launch the users shell. */ launch: function () { - nf.Shell.showPage('users'); + if (nf.Common.canAccessTenants()) { + nf.Shell.showPage('users'); + } } } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js index d14b724bff16..cb329bab76dc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-operate-controller.js @@ -88,6 +88,143 @@ nf.ng.Canvas.OperateCtrl = function () { } }; + /** + * The canvas operator's create template component. + */ + this.templateUpload = { + + /** + * The canvas operator's create template component's modal. + */ + modal: { + + /** + * Gets the modal element. + * + * @returns {*|jQuery|HTMLElement} + */ + getElement: function () { + return $('#upload-template-dialog'); + }, + + /** + * Initialize the modal. + */ + init: function () { + // initialize the form + var templateForm = $('#template-upload-form').ajaxForm({ + url: '../nifi-api/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/templates/upload', + dataType: 'xml', + success: function (response, statusText, xhr, form) { + // see if the import was successful + if (response.documentElement.tagName === 'templateEntity') { + // close the dialog + $('#upload-template-dialog').modal('hide'); + + // close the settings dialog + nf.Dialog.showOkDialog({ + headerText: 'Success', + dialogContent: 'Template successfully imported.' + }); + } else { + // import failed + var status = 'Unable to import template. Please check the log for errors.'; + if (response.documentElement.tagName === 'errorResponse') { + // if a more specific error was given, use it + var errorMessage = response.documentElement.getAttribute('statusText'); + if (!nf.Common.isBlank(errorMessage)) { + status = errorMessage; + } + } + $('#upload-template-status').text(status); + } + }, + error: function (xhr, statusText, error) { + $('#upload-template-status').text(error); + } + }); + + // configure the upload template dialog + this.getElement().modal({ + headerText: 'Upload Template', + buttons: [{ + buttonText: 'Upload', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + // submit the template + templateForm.submit(); + } + } + }, { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + // hide the dialog + $('#upload-template-dialog').modal('hide'); + + // reset the form to ensure that the change fire will fire + templateForm.resetForm(); + } + } + }], + handler: { + close: function () { + // set the filename + $('#selected-template-name').text(''); + $('#upload-template-status').text(''); + } + } + }); + + // add a handler for the change file input chain event + $('#template-file-field').on('change', function (e) { + var filename = $(this).val(); + if (!nf.Common.isBlank(filename)) { + filename = filename.replace(/^.*[\\\/]/, ''); + } + + // set the filename and clear any status + $('#selected-template-name').text(filename); + $('#upload-template-status').text(''); + }); + }, + + /** + * Updates the modal config. + * + * @param {string} name The name of the property to update. + * @param {object|array} config The config for the `name`. + */ + update: function (name, config) { + this.getElement().modal(name, config); + }, + + /** + * Show the modal. + */ + show: function () { + this.getElement().modal('show'); + }, + + /** + * Hide the modal. + */ + hide: function () { + this.getElement().modal('hide'); + } + } + }; + /** * The canvas operator's fillcolor component. */ @@ -275,6 +412,7 @@ nf.ng.Canvas.OperateCtrl = function () { */ init: function () { this.template.modal.init(); + this.templateUpload.modal.init(); this.fillcolor.modal.init(); this.fillcolor.modal.minicolors.init(); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js index 35a3a14820ed..568e1bea1353 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/controllers/nf-ng-canvas-toolbox-controller.js @@ -76,26 +76,13 @@ nf.ng.Canvas.ToolboxCtrl = function (processorComponent, * Initialize the toolbox controller. */ init: function() { - // ensure the user can create graph components - if (nf.Common.isDFM()) { - // initialize modal dialogs - processorComponent.modal.init(); - inputPortComponent.modal.init(); - outputPortComponent.modal.init(); - groupComponent.modal.init(); - remoteGroupComponent.modal.init(); - templateComponent.modal.init(); - } else { - // disable components - processorComponent.disabled(); - inputPortComponent.disabled(); - outputPortComponent.disabled(); - groupComponent.disabled(); - remoteGroupComponent.disabled(); - funnelComponent.disabled(); - templateComponent.disabled(); - labelComponent.disabled(); - } + // initialize modal dialogs + processorComponent.modal.init(); + inputPortComponent.modal.init(); + outputPortComponent.modal.init(); + groupComponent.modal.init(); + remoteGroupComponent.modal.init(); + templateComponent.modal.init(); }, /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js index 3c8a71a118df..b332c5b3b099 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-template-component.js @@ -161,18 +161,20 @@ nf.ng.TemplateComponent = function (serviceProvider) { var self = this; $.ajax({ type: 'GET', - url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/templates', + url: serviceProvider.headerCtrl.toolboxCtrl.config.urls.api + '/flow/templates', dataType: 'json' }).done(function (response) { var templates = response.templates; if (nf.Common.isDefinedAndNotNull(templates) && templates.length > 0) { var options = []; - $.each(templates, function (_, template) { - options.push({ - text: template.name, - value: template.id, - description: nf.Common.escapeHtml(template.description) - }); + $.each(templates, function (_, templateEntity) { + if (templateEntity.accessPolicy.canRead === true) { + options.push({ + text: templateEntity.template.name, + value: templateEntity.id, + description: nf.Common.escapeHtml(templateEntity.template.description) + }); + } }); // configure the templates combo diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js index 747be03f89f2..3cd8c558f670 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js @@ -1169,6 +1169,13 @@ nf.Actions = (function () { nf.CanvasUtils.moveComponentsToParent(selection); }, + /** + * Uploads a new template. + */ + uploadTemplate: function () { + $('#upload-template-dialog').modal('show'); + }, + /** * Creates a new template based off the currently selected components. If no components * are selected, a template of the entire canvas is made. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index 0104b3670ca0..ed9327ea4aa9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -126,8 +126,7 @@ nf.Canvas = (function () { var config = { urls: { api: '../nifi-api', - identity: '../nifi-api/flow/identity', - authorities: '../nifi-api/flow/authorities', + currentUser: '../nifi-api/flow/current-user', kerberos: '../nifi-api/access/kerberos', revision: '../nifi-api/flow/revision', banners: '../nifi-api/flow/banners', @@ -167,54 +166,6 @@ nf.Canvas = (function () { } }; - /** - * Checks the current revision against this version of the flow. - */ - // var checkRevision = function () { - // // get the revision - // return $.ajax({ - // type: 'GET', - // url: config.urls.revision, - // dataType: 'json' - // }).done(function (response) { - // if (nf.Common.isDefinedAndNotNull(response.revision)) { - // var revision = response.revision; - // var currentRevision = nf.Client.getRevision(); - // - // // if there is a newer revision, there are outstanding - // // changes that need to be updated - // if (revision.version > currentRevision.version && revision.clientId !== currentRevision.clientId) { - // var refreshContainer = $('#refresh-required-container'); - // var settingsRefreshIcon = $('#settings-refresh-required-icon'); - // - // // insert the refresh needed text in the canvas - if necessary - // if (!refreshContainer.is(':visible')) { - // $('#stats-last-refreshed').addClass('alert'); - // var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh."; - // - // // update the tooltip - // var refreshRequiredIcon = $('#refresh-required-icon'); - // if (refreshRequiredIcon.data('qtip')) { - // refreshRequiredIcon.qtip('option', 'content.text', refreshMessage); - // } else { - // refreshRequiredIcon.qtip($.extend({ - // content: refreshMessage - // }, nf.CanvasUtils.config.systemTooltipConfig)); - // } - // - // refreshContainer.show(); - // } - // - // // insert the refresh needed text in the settings - if necessary - // if (!settingsRefreshIcon.is(':visible')) { - // $('#settings-last-refreshed').addClass('alert'); - // settingsRefreshIcon.show(); - // } - // } - // } - // }).fail(nf.Common.handleAjaxError); - // }; - /** * Initializes the canvas. */ @@ -563,7 +514,7 @@ nf.Canvas = (function () { evt.preventDefault(); } else if (evt.keyCode === 67) { // ctrl-c - if (nf.Common.isDFM() && nf.CanvasUtils.isCopyable(selection)) { + if (nf.Canvas.canWrite() && nf.CanvasUtils.isCopyable(selection)) { nf.Actions.copy(selection); // only want to prevent default if the action was performed, otherwise default copy would be overridden @@ -571,7 +522,7 @@ nf.Canvas = (function () { } } else if (evt.keyCode === 86) { // ctrl-v - if (nf.Common.isDFM() && nf.CanvasUtils.isPastable()) { + if (nf.Canvas.canWrite() && nf.CanvasUtils.isPastable()) { nf.Actions.paste(selection); // only want to prevent default if the action was performed, otherwise default paste would be overridden @@ -581,7 +532,7 @@ nf.Canvas = (function () { } else { if (evt.keyCode === 8 || evt.keyCode === 46) { // backspace or delete - if (nf.Common.isDFM() && nf.CanvasUtils.areDeletable(selection)) { + if (nf.Canvas.canWrite() && nf.CanvasUtils.areDeletable(selection)) { nf.Actions['delete'](selection); } @@ -755,36 +706,22 @@ nf.Canvas = (function () { } }).promise(); - // load the identity and authorities for the current user + // load the current user var userXhr = $.Deferred(function (deferred) { ticketExchange.always(function () { - // get the current user's identity - var identityXhr = $.ajax({ - type: 'GET', - url: config.urls.identity, - dataType: 'json' - }); - - // get the current user's authorities - var authoritiesXhr = $.ajax({ + // get the current user + $.ajax({ type: 'GET', - url: config.urls.authorities, + url: config.urls.currentUser, dataType: 'json' - }); - - $.when(authoritiesXhr, identityXhr).done(function (authoritiesResult, identityResult) { - var authoritiesResponse = authoritiesResult[0]; - var identityResponse = identityResult[0]; - - // set the user's authorities - nf.Common.setAuthorities(authoritiesResponse.authorities); - + }).done(function (currentUser) { // at this point the user may be themselves or anonymous - + nf.Common.setCurrentUser(currentUser) + // if the user is logged, we want to determine if they were logged in using a certificate - if (identityResponse.anonymous === false) { - // rendner the users name - $('#current-user').text(identityResponse.identity).show(); + if (currentUser.anonymous === false) { + // render the users name + $('#current-user').text(currentUser.identity).show(); // render the logout button if there is a token locally if (nf.Storage.getItem('jwt') !== null) { @@ -805,6 +742,7 @@ nf.Canvas = (function () { }); }); }).promise(); + userXhr.done(function () { // load the client id var clientXhr = nf.Client.init(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js index b5b115697cd9..77258b772e1e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js @@ -172,7 +172,7 @@ nf.ContextMenu = (function () { * @param {selection} selection The selection of currently selected components */ var isPastable = function (selection) { - return nf.Common.isDFM() && nf.CanvasUtils.isPastable(); + return nf.Canvas.canWrite() && nf.CanvasUtils.isPastable(); }; /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js index d7c4c358dd12..e60699b5ee52 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js @@ -580,11 +580,6 @@ nf.ProcessorConfiguration = (function () { dataType: 'json' })); - // get the processor state if we're a DFM - if (nf.Common.isDFM()) { - requests.push(); - } - // once everything is loaded, show the dialog $.when.apply(window, requests).done(function (processorResult, historyResult) { // get the updated processor' diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js index 7cd672075bfa..9907c6c9c9a4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js @@ -156,12 +156,13 @@ nf.RemoteProcessGroupPorts = (function () { }, handler: { click: function () { - // if this is a DFM, the over status of this node may have changed - if (nf.Common.isDFM()) { - // get the component in question - var remoteProcessGroupId = $('#remote-process-group-ports-id').text(); - var remoteProcessGroupData = d3.select('#id-' + remoteProcessGroupId).datum(); + // get the component in question + var remoteProcessGroupId = $('#remote-process-group-ports-id').text(); + var remoteProcessGroup = d3.select('#id-' + remoteProcessGroupId); + var remoteProcessGroupData = remoteProcessGroup.datum(); + // if can modify, the over status of this node may have changed + if (nf.CanvasUtils.canModify(remoteProcessGroup)) { // reload the remote process group nf.RemoteProcessGroup.reload(remoteProcessGroupData.component); } @@ -204,8 +205,12 @@ nf.RemoteProcessGroupPorts = (function () { var portContainerEditContainer = $('
').appendTo(portContainer); var portContainerDetailsContainer = $('
').appendTo(portContainer); - // only DFMs can update the remote group port - if (nf.Common.isDFM()) { + // get the component in question + var remoteProcessGroupId = $('#remote-process-group-ports-id').text(); + var remoteProcessGroup = d3.select('#id-' + remoteProcessGroupId); + + // if can modify, support updating the remote group port + if (nf.CanvasUtils.canModify(remoteProcessGroup)) { // show the enabled transmission switch var transmissionSwitch; if (port.connected === true) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js index 707cecf2789a..cca62cede82c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js @@ -922,6 +922,12 @@ nf.Settings = (function () { * Shows the process group configuration. */ var showSettings = function () { + if (nf.Common.canModifyController()) { + $('#new-service-or-task').show(); + } else { + $('#new-service-or-task').hide(); + } + // show the settings dialog nf.Shell.showContent('#settings').done(function () { reset(); @@ -964,18 +970,20 @@ nf.Settings = (function () { $('#new-service-or-task').hide(); $('#settings-save').show(); } else { - $('#new-service-or-task').show(); - - // update the tooltip on the button - $('#new-service-or-task').attr('title', function () { - if (tab === 'Controller Services') { - $('#settings-save').hide(); - return 'Create a new controller service'; - } else if (tab === 'Reporting Tasks') { - $('#settings-save').hide(); - return 'Create a new reporting task'; - } - }); + if (nf.Common.canModifyController()) { + $('#new-service-or-task').show(); + + // update the tooltip on the button + $('#new-service-or-task').attr('title', function () { + if (tab === 'Controller Services') { + $('#settings-save').hide(); + return 'Create a new controller service'; + } else if (tab === 'Reporting Tasks') { + $('#settings-save').hide(); + return 'Create a new reporting task'; + } + }); + } // resize the table nf.Settings.resetTableSize(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js index 7aa245039b20..5d25e4e4fe2d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js @@ -426,7 +426,7 @@ nf.ClusterTable = (function () { ]; // only allow the admin to modify the cluster - if (nf.Common.isAdmin()) { + if (nf.Common.canModifyController()) { // function for formatting the actions column var actionFormatter = function (row, cell, value, columnDef, dataContext) { var canDisconnect = false; @@ -477,7 +477,7 @@ nf.ClusterTable = (function () { // initialize the sort sort({ - columnId: 'userName', + columnId: 'node', sortAsc: true }, clusterData); @@ -487,7 +487,7 @@ nf.ClusterTable = (function () { clusterGrid.setSortColumn('node', true); clusterGrid.onSort.subscribe(function (e, args) { sort({ - columnId: args.sortCol.field, + columnId: args.sortCol.id, sortAsc: args.sortAsc }, clusterData); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js index 383d8ce247f8..ae672bb1bd5b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js @@ -37,32 +37,21 @@ nf.Cluster = (function () { urls: { banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - authorities: '../nifi-api/flow/authorities' + currentUser: '../nifi-api/flow/current-user' } }; /** - * Loads the current users authorities. + * Loads the current user. */ - var loadAuthorities = function () { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: config.urls.authorities, - dataType: 'json' - }).done(function (response) { - if (nf.Common.isDefinedAndNotNull(response.authorities)) { - // record the users authorities - nf.Common.setAuthorities(response.authorities); - deferred.resolve(); - } else { - deferred.reject(); - } - }).fail(function (xhr, status, error) { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - }); - }).promise(); + var loadCurrentUser = function () { + return $.ajax({ + type: 'GET', + url: config.urls.currentUser, + dataType: 'json' + }).done(function (currentUser) { + nf.Common.setCurrentUser(currentUser); + }).fail(nf.Common.handleAjaxError); }; /** @@ -131,8 +120,8 @@ nf.Cluster = (function () { init: function () { nf.Storage.init(); - // load the users authorities - loadAuthorities().done(function () { + // load the current user + loadCurrentUser().done(function () { // create the counters table nf.ClusterTable.init(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js index 3c32a0de249c..81854ee895d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js @@ -173,7 +173,7 @@ nf.CountersTable = (function () { ]; // only allow dfm's to reset counters - if (nf.Common.isDFM()) { + if (nf.Common.canModifyCounters()) { // function for formatting the actions column var actionFormatter = function (row, cell, value, columnDef, dataContext) { return '
'; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js index 4b7c9210d26e..6a6b36daa988 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js @@ -31,32 +31,22 @@ nf.Counters = (function () { urls: { banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - authorities: '../nifi-api/flow/authorities' + currentUser: '../nifi-api/flow/current-user' } }; /** - * Loads the current users authorities. + * Loads the current user. */ - var loadAuthorities = function () { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: config.urls.authorities, - dataType: 'json' - }).done(function (response) { - if (nf.Common.isDefinedAndNotNull(response.authorities)) { - // record the users authorities - nf.Common.setAuthorities(response.authorities); - deferred.resolve(); - } else { - deferred.reject(); - } - }).fail(function (xhr, status, error) { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - }); - }).promise(); + var loadCurrentUser = function () { + return $.ajax({ + type: 'GET', + url: config.urls.currentUser, + dataType: 'json' + }).done(function (currentUser) { + nf.Common.setCurrentUser(currentUser); + + }).fail(nf.Common.handleAjaxError); }; /** @@ -125,8 +115,8 @@ nf.Counters = (function () { init: function () { nf.Storage.init(); - // load the users authorities - loadAuthorities().done(function () { + // load the current user + loadCurrentUser().done(function () { // create the counters table nf.CountersTable.init(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js index d55d96b63210..366a275b03cf 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js @@ -149,7 +149,7 @@ $('.timezone').text(nf.Common.substringAfterLast(history.lastRefreshed, ' ')); // show the filter message if applicable - if (query['sourceId'] || query['userName'] || query['startDate'] || query['endDate']) { + if (query['sourceId'] || query['userIdentity'] || query['startDate'] || query['endDate']) { $('#history-filter-overview').css('visibility', 'visible'); } else { $('#history-filter-overview').css('visibility', 'hidden'); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js index 52816f6389ef..1892f7b0781a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js @@ -119,7 +119,7 @@ nf.HistoryTable = (function () { if (filterType === 'by id') { filter['sourceId'] = filterText; } else if (filterType === 'by user') { - filter['userName'] = filterText; + filter['userIdentity'] = filterText; } } @@ -274,6 +274,11 @@ nf.HistoryTable = (function () { return '
'; }; + // define how general values are formatted + var valueFormatter = function (row, cell, value, columnDef, dataContext) { + return nf.Common.formatValue(value); + }; + // initialize the templates table var historyColumns = [ { @@ -285,11 +290,11 @@ nf.HistoryTable = (function () { width: 50, maxWidth: 50 }, - {id: 'timestamp', name: 'Date/Time', field: 'timestamp', sortable: true, resizable: true}, - {id: 'sourceName', name: 'Name', field: 'sourceName', sortable: true, resizable: true}, - {id: 'sourceType', name: 'Type', field: 'sourceType', sortable: true, resizable: true}, - {id: 'operation', name: 'Operation', field: 'operation', sortable: true, resizable: true}, - {id: 'userName', name: 'User', field: 'userName', sortable: true, resizable: true} + {id: 'timestamp', name: 'Date/Time', field: 'timestamp', sortable: true, resizable: true, formatter: valueFormatter}, + {id: 'sourceName', name: 'Name', field: 'sourceName', sortable: true, resizable: true, formatter: valueFormatter}, + {id: 'sourceType', name: 'Type', field: 'sourceType', sortable: true, resizable: true, formatter: valueFormatter}, + {id: 'operation', name: 'Operation', field: 'operation', sortable: true, resizable: true, formatter: valueFormatter}, + {id: 'userIdentity', name: 'User', field: 'userIdentity', sortable: true, resizable: true, formatter: valueFormatter} ]; var historyOptions = { forceFitColumns: true, @@ -353,7 +358,7 @@ nf.HistoryTable = (function () { $('#history-table').data('gridInstance', historyGrid); // add the purge button if appropriate - if (nf.Common.isAdmin()) { + if (nf.Common.canModifyController()) { $('#history-purge-button').on('click', function () { $('#history-purge-dialog').modal('show'); }).show(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js index 674e862f6238..c6087bc88447 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js @@ -31,33 +31,21 @@ nf.History = (function () { urls: { banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - authorities: '../nifi-api/flow/authorities' + currentUser: '../nifi-api/flow/current-user' } }; /** - * Loads the current users authorities. + * Loads the current user. */ - var loadAuthorities = function () { - // get the banners and update the page accordingly - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: config.urls.authorities, - dataType: 'json' - }).done(function (response) { - if (nf.Common.isDefinedAndNotNull(response.authorities)) { - // record the users authorities - nf.Common.setAuthorities(response.authorities); - deferred.resolve(); - } else { - deferred.reject(); - } - }).fail(function (xhr, status, error) { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - }); - }).promise(); + var loadCurrentUser = function () { + return $.ajax({ + type: 'GET', + url: config.urls.currentUser, + dataType: 'json' + }).done(function (currentUser) { + nf.Common.setCurrentUser(currentUser); + }).fail(nf.Common.handleAjaxError); }; /** @@ -126,8 +114,8 @@ nf.History = (function () { init: function () { nf.Storage.init(); - // load the users authorities - loadAuthorities().done(function () { + // load the current user + loadCurrentUser().done(function () { // create the history table nf.HistoryTable.init(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js index a200d29a626c..11faba5fb58e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js @@ -25,8 +25,6 @@ nf.Login = (function () { var config = { urls: { - identity: '../nifi-api/flow/identity', - users: '../nifi-api/controller/users', token: '../nifi-api/access/token', accessStatus: '../nifi-api/access', accessConfig: '../nifi-api/access/config' diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 144f8f58cc0a..78729643a18c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -114,17 +114,17 @@ nf.Common = (function () { SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect, /** - * The authorities for the current user. + * The current user. */ - authorities: undefined, + currentUser: undefined, /** - * Sets the authorities for the current user. + * Sets the current user. * - * @argument {array} roles The current users authorities + * @param currentUser */ - setAuthorities: function (roles) { - nf.Common.authorities = roles; + setCurrentUser: function (currentUser) { + nf.Common.currentUser = currentUser; }, /** @@ -226,48 +226,89 @@ nf.Common = (function () { * @returns {boolean} */ canAccessProvenance: function () { - var canAccessProvenance = false; - if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) { - $.each(nf.Common.authorities, function (i, authority) { - if (authority === 'ROLE_PROVENANCE') { - canAccessProvenance = true; - return false; - } - }); + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.provenancePermissions.canRead === true; + } else { + return false; } - return canAccessProvenance; }, /** - * Returns whether or not the current user is a DFM. + * Determines whether the current user can access counters. + * + * @returns {boolean} */ - isDFM: function () { - var dfm = false; - if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) { - $.each(nf.Common.authorities, function (i, authority) { - if (authority === 'ROLE_DFM') { - dfm = true; - return false; - } - }); + canAccessCounters: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.countersPermissions.canRead === true; + } else { + return false; } - return dfm; }, /** - * Returns whether or not the current user is a DFM. + * Determines whether the current user can modify counters. + * + * @returns {boolean} */ - isAdmin: function () { - var admin = false; - if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) { - $.each(nf.Common.authorities, function (i, authority) { - if (authority === 'ROLE_ADMIN') { - admin = true; - return false; - } - }); + canModifyCounters: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.countersPermissions.canRead === true && nf.Common.currentUser.countersPermissions.canWrite === true; + } else { + return false; + } + }, + + /** + * Determines whether the current user can access tenants. + * + * @returns {boolean} + */ + canAccessTenants: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.tenantsPermissions.canRead === true; + } else { + return false; + } + }, + + /** + * Determines whether the current user can modify tenants. + * + * @returns {boolean} + */ + canModifyTenants: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.tenantsPermissions.canRead === true && nf.Common.currentUser.tenantsPermissions.canWrite === true; + } else { + return false; + } + }, + + /** + * Determines whether the current user can access the controller. + * + * @returns {boolean} + */ + canAccessController: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.controllerPermissions.canRead === true; + } else { + return false; + } + }, + + /** + * Determines whether the current user can modify the controller. + * + * @returns {boolean} + */ + canModifyController: function () { + if (nf.Common.isDefinedAndNotNull(nf.Common.currentUser)) { + return nf.Common.currentUser.controllerPermissions.canRead === true && nf.Common.currentUser.controllerPermissions.canWrite === true; + } else { + return false; } - return admin; }, /** @@ -508,7 +549,7 @@ nf.Common = (function () { if (!nf.Common.isEmpty(propertyHistory.previousValues)) { var history = []; $.each(propertyHistory.previousValues, function (_, previousValue) { - history.push('
  • ' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userName) + ')
  • '); + history.push('
  • ' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userIdentity) + ')
  • '); }); tipContent.push('History:
      ' + history.join('') + '
    '); } 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 04a220421d6e..5d1d76a7ac24 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 @@ -26,7 +26,7 @@ nf.ProvenanceLineage = (function () { sliderTickCount: 75, urls: { lineage: '../nifi-api/provenance/lineage', - events: '../nifi-api/provenance/events/' + events: '../nifi-api/provenance-events/' } }; 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 f0248661ee1e..9f739a3408a1 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 @@ -33,9 +33,10 @@ nf.ProvenanceTable = (function () { }, urls: { searchOptions: '../nifi-api/provenance/search-options', - replays: '../nifi-api/provenance/replays', + replays: '../nifi-api/provenance-events/replays', provenance: '../nifi-api/provenance', - cluster: '../nifi-api/controller/cluster', + provenanceEvents: '../nifi-api/provenance-events', + clusterSearch: '../nifi-api/flow/cluster/search-results', d3Script: 'js/d3/d3.min.js', lineageScript: 'js/nf/provenance/nf-provenance-lineage.js', uiExtensionToken: '../nifi-api/access/ui-extension-token', @@ -57,7 +58,7 @@ nf.ProvenanceTable = (function () { var eventId = $('#provenance-event-id').text(); // build the url - var dataUri = config.urls.provenance + '/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); + var dataUri = config.urls.provenanceEvents + '/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); // perform the request once we've received a token nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) { @@ -98,7 +99,7 @@ nf.ProvenanceTable = (function () { var eventId = $('#provenance-event-id').text(); // build the uri to the data - var dataUri = controllerUri + 'provenance/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); + var dataUri = config.urls.provenanceEvents + '/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); // generate tokens as necessary var getAccessTokens = $.Deferred(function (deferred) { @@ -258,38 +259,35 @@ nf.ProvenanceTable = (function () { } // handle the replay and downloading - if (nf.Common.isDFM()) { - // replay - $('#replay-content').on('click', function () { - var replayEntity = { - 'eventId': $('#provenance-event-id').text() - }; + $('#replay-content').on('click', function () { + var replayEntity = { + 'eventId': $('#provenance-event-id').text() + }; - // conditionally include the cluster node id - var clusterNodeId = $('#provenance-event-cluster-node-id').text(); - if (!nf.Common.isBlank(clusterNodeId)) { - replayEntity['clusterNodeId'] = clusterNodeId; - } + // conditionally include the cluster node id + var clusterNodeId = $('#provenance-event-cluster-node-id').text(); + if (!nf.Common.isBlank(clusterNodeId)) { + replayEntity['clusterNodeId'] = clusterNodeId; + } - $.ajax({ - type: 'POST', - url: config.urls.replays, - data: JSON.stringify(replayEntity), - dataType: 'json', - contentType: 'application/json' - }).done(function (response) { - nf.Dialog.showOkDialog({ - headerText: 'Provenance', - dialogContent: 'Successfully submitted replay request.' - }); - }).fail(nf.Common.handleAjaxError); + $.ajax({ + type: 'POST', + url: config.urls.replays, + data: JSON.stringify(replayEntity), + dataType: 'json', + contentType: 'application/json' + }).done(function (response) { + nf.Dialog.showOkDialog({ + headerText: 'Provenance', + dialogContent: 'Successfully submitted replay request.' + }); + }).fail(nf.Common.handleAjaxError); - $('#event-details-dialog').modal('hide'); - }); + $('#event-details-dialog').modal('hide'); + }); - // show the replay panel - $('#replay-details').show(); - } + // show the replay panel + $('#replay-details').show(); }; /** @@ -323,11 +321,10 @@ nf.ProvenanceTable = (function () { // get the nodes in the cluster $.ajax({ type: 'GET', - url: config.urls.cluster, + url: config.urls.clusterSearch, dataType: 'json' }).done(function (response) { - var cluster = response.cluster; - var nodes = cluster.nodes; + var nodeResults = response.nodeResults; // create the searchable options var searchableOptions = [{ @@ -336,17 +333,17 @@ nf.ProvenanceTable = (function () { }]; // sort the nodes - nodes.sort(function (a, b) { - var compA = (a.address + ':' + a.apiPort).toUpperCase(); - var compB = (b.address + ':' + b.apiPort).toUpperCase(); + nodeResults.sort(function (a, b) { + var compA = a.address.toUpperCase(); + var compB = b.address.toUpperCase(); return (compA < compB) ? -1 : (compA > compB) ? 1 : 0; }); // add each node - $.each(nodes, function (_, node) { + $.each(nodeResults, function (_, nodeResult) { searchableOptions.push({ - text: node.address + ':' + node.apiPort, - value: node.nodeId + text: nodeResult.address, + value: nodeResult.id }); }); @@ -1430,15 +1427,13 @@ nf.ProvenanceTable = (function () { $('#output-content-view').hide(); } - if (nf.Common.isDFM()) { - if (event.replayAvailable === true) { - $('#replay-content, #replay-content-connection').show(); - formatContentValue($('#replay-connection-id'), event.sourceConnectionIdentifier); - $('#replay-content-message').hide(); - } else { - $('#replay-content, #replay-content-connection').hide(); - $('#replay-content-message').text(event.replayExplanation).show(); - } + if (event.replayAvailable === true) { + $('#replay-content, #replay-content-connection').show(); + formatContentValue($('#replay-connection-id'), event.sourceConnectionIdentifier); + $('#replay-content-message').hide(); + } else { + $('#replay-content, #replay-content-connection').hide(); + $('#replay-content-message').text(event.replayExplanation).show(); } // show the dialog 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 a04aad7216c3..fe16705744a1 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 @@ -32,7 +32,7 @@ nf.Provenance = (function () { flowConfig: '../nifi-api/flow/config', banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - authorities: '../nifi-api/flow/authorities' + currentUser: '../nifi-api/flow/current-user' } }; @@ -81,27 +81,16 @@ nf.Provenance = (function () { }; /** - * Loads the current users authorities. + * Loads the current user. */ - var loadAuthorities = function () { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: config.urls.authorities, - dataType: 'json' - }).done(function (response) { - if (nf.Common.isDefinedAndNotNull(response.authorities)) { - // record the users authorities - nf.Common.setAuthorities(response.authorities); - deferred.resolve(response); - } else { - deferred.reject(); - } - }).fail(function (xhr, status, error) { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - }); - }).promise(); + var loadCurrentUser = function () { + return $.ajax({ + type: 'GET', + url: config.urls.currentUser, + dataType: 'json' + }).done(function (currentUser) { + nf.Common.setCurrentUser(currentUser); + }).fail(nf.Common.handleAjaxError); }; /** @@ -170,8 +159,8 @@ nf.Provenance = (function () { init: function () { nf.Storage.init(); - // load the users authorities and detect if the NiFi is clustered - $.when(loadAbout(), loadAuthorities(), detectedCluster()).done(function () { + // load the user and detect if the NiFi is clustered + $.when(loadAbout(), loadCurrentUser(), detectedCluster()).done(function () { // create the provenance table nf.ProvenanceTable.init(isClustered).done(function () { var searchTerms = {}; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js index 8162c2cd8a2b..eedeed27c307 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js @@ -21,7 +21,7 @@ nf.ClusterSearch = (function () { var config = { search: 'Search nodes', urls: { - clusterSearch: '../nifi-api/controller/cluster/search-results' + clusterSearch: '../nifi-api/flow/cluster/search-results' } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js index 81b80b72d8bd..5477a31e685f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js @@ -28,16 +28,11 @@ nf.TemplatesTable = (function () { filterList: 'templates-filter-list' }, urls: { - api: '../nifi-api', + templates: '../nifi-api/flow/templates', downloadToken: '../nifi-api/access/download-token' } }; - /** - * the current group id - */ - var groupId; - /** * Sorts the specified data using the specified sort details. * @@ -52,8 +47,8 @@ nf.TemplatesTable = (function () { var bDate = nf.Common.parseDateTime(b[sortDetails.columnId]); return aDate.getTime() - bDate.getTime(); } else { - var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; - var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; + var aString = nf.Common.isDefinedAndNotNull(a.template[sortDetails.columnId]) ? a.template[sortDetails.columnId] : ''; + var bString = nf.Common.isDefinedAndNotNull(b.template[sortDetails.columnId]) ? b.template[sortDetails.columnId] : ''; return aString === bString ? 0 : aString > bString ? 1 : -1; } }; @@ -65,15 +60,15 @@ nf.TemplatesTable = (function () { /** * Prompts the user before attempting to delete the specified template. * - * @argument {object} template The template + * @argument {object} templateEntity The template */ - var promptToDeleteTemplate = function (template) { + var promptToDeleteTemplate = function (templateEntity) { // prompt for deletion nf.Dialog.showYesNoDialog({ headerText: 'Delete Template', - dialogContent: 'Delete template \'' + nf.Common.escapeHtml(template.name) + '\'?', + dialogContent: 'Delete template \'' + nf.Common.escapeHtml(templateEntity.template.name) + '\'?', yesHandler: function () { - deleteTemplate(template); + deleteTemplate(templateEntity); } }); }; @@ -81,17 +76,17 @@ nf.TemplatesTable = (function () { /** * Deletes the template with the specified id. * - * @argument {string} template The template + * @argument {string} templateEntity The template */ - var deleteTemplate = function (template) { + var deleteTemplate = function (templateEntity) { $.ajax({ type: 'DELETE', - url: template.uri, + url: templateEntity.template.uri, dataType: 'json' }).done(function () { var templatesGrid = $('#templates-table').data('gridInstance'); var templatesData = templatesGrid.getData(); - templatesData.deleteItem(template.id); + templatesData.deleteItem(templateEntity.id); // update the total number of templates $('#total-templates').text(templatesData.getItems().length); @@ -153,15 +148,15 @@ nf.TemplatesTable = (function () { } // perform the filter - return item[args.property].search(filterExp) >= 0; + return item.template[args.property].search(filterExp) >= 0; }; /** * Downloads the specified template. * - * @param {object} template The template + * @param {object} templateEntity The template */ - var downloadTemplate = function (template) { + var downloadTemplate = function (templateEntity) { nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) { var parameters = {}; @@ -172,9 +167,9 @@ nf.TemplatesTable = (function () { // open the url if ($.isEmptyObject(parameters)) { - window.open(template.uri + '/download'); + window.open(templateEntity.template.uri + '/download'); } else { - window.open(template.uri + '/download' + '?' + $.param(parameters)); + window.open(templateEntity.template.uri + '/download' + '?' + $.param(parameters)); } }).fail(function () { nf.Dialog.showOkDialog({ @@ -216,22 +211,48 @@ nf.TemplatesTable = (function () { } }); - // enable template uploading if DFM - if (nf.Common.isDFM()) { - $('#upload-template-container').show(); - } + var timestampFormatter = function (row, cell, value, columnDef, dataContext) { + if (!dataContext.accessPolicy.canRead) { + return ''; + } - // function for formatting the last accessed time - var valueFormatter = function (row, cell, value, columnDef, dataContext) { - return nf.Common.formatValue(value); + return dataContext.template.timestamp; + }; + + var nameFormatter = function (row, cell, value, columnDef, dataContext) { + if (!dataContext.accessPolicy.canRead) { + return '' + dataContext.id + ''; + } + + return dataContext.template.name; + }; + + var descriptionFormatter = function (row, cell, value, columnDef, dataContext) { + if (!dataContext.accessPolicy.canRead) { + return ''; + } + + return nf.Common.formatValue(dataContext.template.description); + }; + + var groupIdFormatter = function (row, cell, value, columnDef, dataContext) { + if (!dataContext.accessPolicy.canRead) { + return ''; + } + + return dataContext.template.groupId; }; // function for formatting the actions column var actionFormatter = function (row, cell, value, columnDef, dataContext) { - var markup = '
    '; + var markup = ''; + + if (dataContext.accessPolicy.canRead === true) { + markup += '
    '; + } // all DFMs to remove templates - if (nf.Common.isDFM()) { + if (dataContext.accessPolicy.canWrite === true) { markup += '
    '; } return markup; @@ -239,9 +260,10 @@ nf.TemplatesTable = (function () { // initialize the templates table var templatesColumns = [ - {id: 'timestamp', name: 'Date/Time', field: 'timestamp', sortable: true, defaultSortAsc: false, resizable: false, formatter: valueFormatter, width: 225, maxWidth: 225}, - {id: 'name', name: 'Name', field: 'name', sortable: true, resizable: true}, - {id: 'description', name: 'Description', field: 'description', sortable: true, resizable: true, formatter: valueFormatter}, + {id: 'timestamp', name: 'Date/Time', sortable: true, defaultSortAsc: false, resizable: false, formatter: timestampFormatter, width: 225, maxWidth: 225}, + {id: 'name', name: 'Name', sortable: true, resizable: true, formatter: nameFormatter}, + {id: 'description', name: 'Description', sortable: true, resizable: true, formatter: descriptionFormatter}, + {id: 'groupId', name: 'Process Group Id', sortable: true, resizable: true, formatter: groupIdFormatter}, {id: 'actions', name: ' ', sortable: false, resizable: false, formatter: actionFormatter, width: 100, maxWidth: 100} ]; var templatesOptions = { @@ -277,7 +299,7 @@ nf.TemplatesTable = (function () { templatesGrid.setSortColumn('timestamp', false); templatesGrid.onSort.subscribe(function (e, args) { sort({ - columnId: args.sortCol.field, + columnId: args.sortCol.id, sortAsc: args.sortAsc }, templatesData); }); @@ -333,17 +355,9 @@ nf.TemplatesTable = (function () { * Load the processor templates table. */ loadTemplatesTable: function () { - groupId = $('#template-group-id').text(); - if (nf.Common.isUndefined(groupId) || nf.Common.isNull(groupId)) { - nf.Dialog.showOkDialog({ - headerText: 'Load Templates', - content: 'Group id not specified.' - }); - } - return $.ajax({ type: 'GET', - url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId) + '/templates', + url: config.urls.templates, data: { verbose: false }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js index 86d9934506f0..678c25f91491 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js @@ -31,37 +31,21 @@ nf.Templates = (function () { urls: { banners: '../nifi-api/flow/banners', about: '../nifi-api/flow/about', - authorities: '../nifi-api/flow/authorities' + currentUser: '../nifi-api/flow/current-user' } }; /** - * the current group id + * Loads the current users. */ - var groupId; - - /** - * Loads the current users authorities. - */ - var loadAuthorities = function () { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: config.urls.authorities, - dataType: 'json' - }).done(function (response) { - if (nf.Common.isDefinedAndNotNull(response.authorities)) { - // record the users authorities - nf.Common.setAuthorities(response.authorities); - deferred.resolve(); - } else { - deferred.reject(); - } - }).fail(function (xhr, status, error) { - nf.Common.handleAjaxError(xhr, status, error); - deferred.reject(); - }); - }).promise(); + var loadCurrentUser = function () { + return $.ajax({ + type: 'GET', + url: config.urls.currentUser, + dataType: 'json' + }).done(function (currentUser) { + nf.Common.setCurrentUser(currentUser); + }).fail(nf.Common.handleAjaxError); }; /** @@ -73,81 +57,6 @@ nf.Templates = (function () { nf.TemplatesTable.loadTemplatesTable(); }); - // add a handler for the change file input chain event - $('#template-file-field').on('change', function (e) { - var filename = $(this).val(); - if (!nf.Common.isBlank(filename)) { - filename = filename.replace(/^.*[\\\/]/, ''); - } - - // set the filename - $('#selected-template-name').text(filename); - - // update the container visibility - $('#select-template-container').hide(); - $('#submit-template-container').show(); - }); - - // handles any uploading error - could be an error response or a successful response with an encoded error - var handleError = function (error) { - // show any errors - $('#upload-template-status').removeClass('import-status').addClass('import-status-error').text(error); - - // clear the form - $('#cancel-upload-template-button').click(); - }; - - // initialize the form - var templateForm = $('#template-upload-form').ajaxForm({ - url: '../nifi-api/process-groups/' + encodeURIComponent(groupId) + '/templates/upload', - dataType: 'xml', - success: function (response, statusText, xhr, form) { - // see if the import was successful - if (response.documentElement.tagName === 'templateEntity') { - // reset the status message - $('#upload-template-status').removeClass('import-status-error').addClass('import-status'); - - // clear the form - $('#cancel-upload-template-button').click(); - - // reload the templates table - nf.TemplatesTable.loadTemplatesTable(); - } else { - // import failed - var status = 'Unable to import template. Please check the log for errors.'; - if (response.documentElement.tagName === 'errorResponse') { - // if a more specific error was given, use it - var errorMessage = response.documentElement.getAttribute('statusText'); - if (!nf.Common.isBlank(errorMessage)) { - status = errorMessage; - } - } - handleError(status); - } - }, - error: function (xhr, statusText, error) { - handleError(error); - } - }); - - // add a handler for the upload button - $('#upload-template-button').click(function () { - templateForm.submit(); - }); - - // add a handler for the cancel upload button - $('#cancel-upload-template-button').click(function () { - // set the filename - $('#selected-template-name').text(''); - - // reset the form to ensure that the change fire will fire - templateForm.resetForm(); - - // update the container visibility - $('#select-template-container').show(); - $('#submit-template-container').hide(); - }); - // get the banners if we're not in the shell return $.Deferred(function (deferred) { if (top === window) { @@ -204,17 +113,8 @@ nf.Templates = (function () { init: function () { nf.Storage.init(); - // ensure the group id is specified - groupId = $('#template-group-id').text(); - if (nf.Common.isUndefined(groupId) || nf.Common.isNull(groupId)) { - nf.Dialog.showOkDialog({ - headerText: 'Load Templates', - content: 'Group id not specified.' - }); - } - - // load the users authorities - loadAuthorities().done(function () { + // load the current user + loadCurrentUser().done(function () { // create the templates table nf.TemplatesTable.init(); 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 adb335c4c0a5..3b827eba4caf 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 @@ -16,51 +16,6 @@ */ package org.apache.nifi.provenance; -import java.io.EOFException; -import java.io.File; -import java.io.FileFilter; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; -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; import org.apache.lucene.index.IndexNotFoundException; @@ -111,6 +66,51 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.EOFException; +import java.io.File; +import java.io.FileFilter; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +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; + public class PersistentProvenanceRepository implements ProvenanceEventRepository { public static final String DEPRECATED_CLASS_NAME = "nifi.controller.repository.provenance.PersistentProvenanceRepository"; @@ -416,7 +416,7 @@ public boolean isAuthorized(final ProvenanceEventRecord event, final NiFiUser us return false; } - final AuthorizationResult result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, user); + final AuthorizationResult result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, user, event.getAttributes()); return Result.Approved.equals(result.getResult()); } @@ -426,7 +426,7 @@ protected void authorize(final ProvenanceEventRecord event, final NiFiUser user) } final Authorizable eventAuthorizable = resourceFactory.createProvenanceAuthorizable(event.getComponentId()); - eventAuthorizable.authorize(authorizer, RequestAction.READ, user); + eventAuthorizable.authorize(authorizer, RequestAction.READ, user, event.getAttributes()); } private List filterUnauthorizedEvents(final List events, final NiFiUser user) { diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/test/java/org/apache/nifi/provenance/TestPersistentProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/test/java/org/apache/nifi/provenance/TestPersistentProvenanceRepository.java index 54176699e6b8..835e06ed90f2 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/test/java/org/apache/nifi/provenance/TestPersistentProvenanceRepository.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/test/java/org/apache/nifi/provenance/TestPersistentProvenanceRepository.java @@ -16,33 +16,6 @@ */ package org.apache.nifi.provenance; -import static org.apache.nifi.provenance.TestUtil.createFlowFile; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; -import java.util.zip.GZIPOutputStream; - import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.core.SimpleAnalyzer; import org.apache.lucene.document.Document; @@ -85,6 +58,33 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.io.File; +import java.io.FileFilter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; + +import static org.apache.nifi.provenance.TestUtil.createFlowFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + public class TestPersistentProvenanceRepository { @Rule @@ -1734,11 +1734,6 @@ public String getIdentity() { return "unit-test"; } - @Override - public String getUserName() { - return "Unit Test"; - } - @Override public NiFiUser getChain() { return null; diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java index 888f55a10e88..ce53393e82a3 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/main/java/org/apache/nifi/provenance/VolatileProvenanceRepository.java @@ -16,27 +16,6 @@ */ package org.apache.nifi.provenance; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.regex.Pattern; - import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.AuthorizationResult; import org.apache.nifi.authorization.AuthorizationResult.Result; @@ -64,6 +43,27 @@ import org.apache.nifi.util.RingBuffer.IterationDirection; import org.apache.nifi.web.ResourceNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Pattern; + public class VolatileProvenanceRepository implements ProvenanceEventRepository { // properties @@ -246,7 +246,7 @@ public boolean isAuthorized(final ProvenanceEventRecord event, final NiFiUser us return false; } - final AuthorizationResult result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, user); + final AuthorizationResult result = eventAuthorizable.checkAuthorization(authorizer, RequestAction.READ, user, event.getAttributes()); return Result.Approved.equals(result.getResult()); } @@ -256,7 +256,7 @@ protected void authorize(final ProvenanceEventRecord event, final NiFiUser user) } final Authorizable eventAuthorizable = resourceFactory.createProvenanceAuthorizable(event.getComponentId()); - eventAuthorizable.authorize(authorizer, RequestAction.READ, user); + eventAuthorizable.authorize(authorizer, RequestAction.READ, user, event.getAttributes()); } private Filter createFilter(final Query query, final NiFiUser user) { diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/test/java/org/apache/nifi/provenance/TestVolatileProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/test/java/org/apache/nifi/provenance/TestVolatileProvenanceRepository.java index d35ceace0d98..7a7a334a2ce0 100644 --- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/test/java/org/apache/nifi/provenance/TestVolatileProvenanceRepository.java +++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-volatile-provenance-repository/src/test/java/org/apache/nifi/provenance/TestVolatileProvenanceRepository.java @@ -16,7 +16,14 @@ */ package org.apache.nifi.provenance; -import static org.junit.Assert.assertEquals; +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.flowfile.FlowFile; +import org.apache.nifi.provenance.search.Query; +import org.apache.nifi.provenance.search.QuerySubmission; +import org.apache.nifi.provenance.search.SearchTerms; +import org.apache.nifi.util.NiFiProperties; +import org.junit.BeforeClass; +import org.junit.Test; import java.io.IOException; import java.util.HashMap; @@ -26,15 +33,7 @@ import java.util.Set; import java.util.UUID; -import org.apache.nifi.authorization.user.NiFiUser; -import org.apache.nifi.flowfile.FlowFile; -import org.apache.nifi.provenance.search.Query; -import org.apache.nifi.provenance.search.QuerySubmission; -import org.apache.nifi.provenance.search.SearchTerms; -import org.apache.nifi.util.NiFiProperties; - -import org.junit.BeforeClass; -import org.junit.Test; +import static org.junit.Assert.assertEquals; public class TestVolatileProvenanceRepository { @@ -183,11 +182,6 @@ public String getIdentity() { return "unit-test"; } - @Override - public String getUserName() { - return "Unit Test"; - } - @Override public NiFiUser getChain() { return null; diff --git a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/processors/script/ExecuteScriptGroovyTest.groovy b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/processors/script/ExecuteScriptGroovyTest.groovy index e7e9d484222e..86f119af17fd 100644 --- a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/processors/script/ExecuteScriptGroovyTest.groovy +++ b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/processors/script/ExecuteScriptGroovyTest.groovy @@ -69,9 +69,9 @@ class ExecuteScriptGroovyTest extends BaseScriptTest { runner.setProperty(ExecuteScript.SCRIPT_FILE, TEST_RESOURCE_LOCATION + "groovy/testAddTimeAndThreadAttribute.groovy") runner.setProperty(ExecuteScript.MODULES, TEST_RESOURCE_LOCATION + "groovy") - // Override context value + // Override userContext value runner.processContext.maxConcurrentTasks = poolSize - logger.info("Overrode context max concurrent tasks to ${runner.processContext.maxConcurrentTasks}") + logger.info("Overrode userContext max concurrent tasks to ${runner.processContext.maxConcurrentTasks}") } @Test