This is an automated email from the ASF dual-hosted git repository. exceptionfactory pushed a commit to branch support/nifi-1.x in repository https://gitbox.apache.org/repos/asf/nifi.git
commit 207af04ea292cd61090eafb2842ca66c5ac78dcf Author: dan-s1 <[email protected]> AuthorDate: Mon Jun 26 12:09:57 2023 -0500 NIFI-11747 Refactored Groovy tests in nifi-web-api to Java (and JUnit 5) This closes #7434 Signed-off-by: David Handermann <[email protected]> (cherry picked from commit 7748d9d1e5287c6b99c965ee0ed93ed154802def) --- .../nifi-framework/nifi-web/nifi-web-api/pom.xml | 15 - .../nifi/web/StandardNiFiServiceFacadeSpec.groovy | 1137 -------------------- .../nifi/web/api/ApplicationResourceTest.groovy | 176 --- .../nifi/web/api/ProcessGroupResourceTest.groovy | 154 --- ...JsonContentConversionExceptionMapperTest.groovy | 86 -- .../StandardPolicyBasedAuthorizerDAOSpec.groovy | 618 ----------- .../web/dao/impl/StandardTemplateDAOSpec.groovy | 160 --- .../nifi/web/api/TestApplicationResource.java | 189 ++++ .../nifi/web/api/TestProcessGroupResource.java | 75 +- .../JsonContentConversionExceptionMapperTest.java | 74 ++ 10 files changed, 325 insertions(+), 2359 deletions(-) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml index eb06edf680..d87e41ca21 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml @@ -20,7 +20,6 @@ <artifactId>nifi-web</artifactId> <version>1.23.0-SNAPSHOT</version> </parent> - <groupId>org.apache.nifi</groupId> <artifactId>nifi-web-api</artifactId> <packaging>war</packaging> <properties> @@ -410,20 +409,6 @@ <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.codehaus.groovy</groupId> - <artifactId>groovy-test</artifactId> - </dependency> - <dependency> - <groupId>org.spockframework</groupId> - <artifactId>spock-core</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.hamcrest</groupId> - <artifactId>hamcrest-all</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-mock</artifactId> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy deleted file mode 100644 index 0df27ab58a..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/StandardNiFiServiceFacadeSpec.groovy +++ /dev/null @@ -1,1137 +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 - -import io.prometheus.client.CollectorRegistry -import io.prometheus.client.exporter.common.TextFormat -import org.apache.nifi.authorization.AccessDeniedException -import org.apache.nifi.authorization.AccessPolicy -import org.apache.nifi.authorization.AuthorizableLookup -import org.apache.nifi.authorization.AuthorizationResult -import org.apache.nifi.authorization.Authorizer -import org.apache.nifi.authorization.Group -import org.apache.nifi.authorization.RequestAction -import org.apache.nifi.authorization.Resource -import org.apache.nifi.authorization.User -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.StandardNiFiUser -import org.apache.nifi.connectable.Connection -import org.apache.nifi.controller.flow.StandardFlowManager -import org.apache.nifi.controller.repository.FlowFileEvent -import org.apache.nifi.controller.repository.FlowFileEventRepository -import org.apache.nifi.controller.service.ControllerServiceProvider -import org.apache.nifi.controller.status.PortStatus -import org.apache.nifi.controller.status.ProcessGroupStatus -import org.apache.nifi.controller.status.RunStatus -import org.apache.nifi.diagnostics.StorageUsage -import org.apache.nifi.diagnostics.SystemDiagnostics -import org.apache.nifi.groups.ProcessGroup -import org.apache.nifi.reporting.Bulletin -import org.apache.nifi.reporting.BulletinRepository -import org.apache.nifi.util.MockBulletinRepository -import org.apache.nifi.web.api.dto.AccessPolicyDTO -import org.apache.nifi.web.api.dto.BulletinDTO -import org.apache.nifi.web.api.dto.DtoFactory -import org.apache.nifi.web.api.dto.EntityFactory -import org.apache.nifi.web.api.dto.PermissionsDTO -import org.apache.nifi.web.api.dto.RevisionDTO -import org.apache.nifi.web.api.dto.UserDTO -import org.apache.nifi.web.api.dto.UserGroupDTO -import org.apache.nifi.web.api.dto.status.StatusHistoryDTO -import org.apache.nifi.web.api.entity.BulletinEntity -import org.apache.nifi.web.api.entity.StatusHistoryEntity -import org.apache.nifi.web.api.entity.UserEntity -import org.apache.nifi.web.controller.ControllerFacade -import org.apache.nifi.web.dao.AccessPolicyDAO -import org.apache.nifi.web.dao.ProcessGroupDAO -import org.apache.nifi.web.dao.UserDAO -import org.apache.nifi.web.dao.UserGroupDAO -import org.apache.nifi.web.revision.DeleteRevisionTask -import org.apache.nifi.web.revision.ReadOnlyRevisionCallback -import org.apache.nifi.web.revision.RevisionClaim -import org.apache.nifi.web.revision.RevisionManager -import org.apache.nifi.web.revision.UpdateRevisionTask -import org.apache.nifi.web.security.token.NiFiAuthenticationToken -import org.springframework.security.core.context.SecurityContextHolder -import spock.lang.Ignore -import spock.lang.Specification -import spock.lang.Unroll - -class StandardNiFiServiceFacadeSpec extends Specification { - - def setup() { - final NiFiUser user = new StandardNiFiUser.Builder().identity("nifi-user").build(); - final NiFiAuthenticationToken auth = new NiFiAuthenticationToken(new NiFiUserDetails(user)); - SecurityContextHolder.getContext().setAuthentication(auth); - } - - def cleanup() { - SecurityContextHolder.getContext().setAuthentication(null); - } - - @Ignore - @Unroll - def "CreateUser: isAuthorized: #isAuthorized"() { - given: - def userDao = Mock UserDAO - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - def authorizableLookup = Mock AuthorizableLookup - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setUserDAO userDao - niFiServiceFacade.setEntityFactory entityFactory - def newUser = new User.Builder().identifier(userDto.id).identity(userDto.identity).build() - - when: - def userEntity = niFiServiceFacade.createUser(new Revision(0L, 'client-1', userDto.id), userDto) - - then: - 1 * userDao.createUser(_) >> newUser - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(parentAuthorizable, resource, isAuthorized, authorizationResult) - 0 * _ - userEntity != null - if (isAuthorized) { - assert userEntity?.component?.id == userDto.id - assert userEntity?.component?.identity?.equals(userDto.identity) - assert userEntity?.permissions?.canRead - assert userEntity?.permissions?.canWrite - } else { - assert userEntity.component == null - } - - - where: - userDto | parentAuthorizable | resource | isAuthorized | authorizationResult - createUserDTO() | null | ResourceFactory.usersResource | true | AuthorizationResult.approved() - createUserDTO() | null | ResourceFactory.usersResource | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "GetUser: isAuthorized: #isAuthorized"() { - given: - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setUserDAO userDao - def requestedUser = new User.Builder().identifier(userDto.id).identity(userDto.identity).build() - def exception = null - def userEntity = null - - when: - try { - userEntity = niFiServiceFacade.getUser(userDto.id, true) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * userDao.getUser(userDto.id) >> requestedUser - 1 * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult) - 0 * _ - - assert userEntity != null - if (isAuthorized) { - assert userEntity.component?.id?.equals(userDto.id) - } else { - assert userEntity.component == null - } - - where: - userDto | isAuthorized | authorizationResult - createUserDTO() | true | AuthorizationResult.approved() - createUserDTO() | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "UpdateUser: isAuthorized: #isAuthorized, policy exists: #userExists"() { - given: - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - def authorizableLookup = Mock AuthorizableLookup - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setUserDAO userDao - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setControllerFacade controllerFacade - def user = new User.Builder().identifier(userDto.id).identity(userDto.identity).build() - - when: - def userEntityUpdateResult = niFiServiceFacade.updateUser(currentRevision, userDto) - - then: - 1 * userDao.hasUser(userDto.id) >> userExists - if (!userExists) { - 1 * userDao.createUser(userDto) >> user - } else { - 1 * controllerFacade.save() - 1 * userDao.updateUser(userDto) >> user - 1 * revisionManager.updateRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser niFiUser, UpdateRevisionTask callback -> - callback.update() - } - 1 * revisionManager.getRevision(currentRevision.componentId) >> currentRevision.incrementRevision(currentRevision.clientId) - } - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult) - 0 * _ - userEntityUpdateResult != null - def userEntity = userEntityUpdateResult?.result - if (isAuthorized) { - assert userEntity?.component?.getIdentifier?.equals(userDto.id) - assert userEntity?.getPermissions?.canRead - assert userEntity?.getPermissions?.canWrite - } else { - assert userEntity.component == null - } - - where: - userExists | currentRevision | userDto | isAuthorized | authorizationResult - false | new Revision(0L, 'client1', 'root') | createUserDTO() | true | AuthorizationResult.approved() - true | new Revision(1L, 'client1', 'root') | createUserDTO() | true | AuthorizationResult.approved() - false | new Revision(0L, 'client1', 'root') | createUserDTO() | false | AuthorizationResult.denied() - true | new Revision(1L, 'client1', 'root') | createUserDTO() | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "DeleteUser: isAuthorized: #isAuthorized, user exists: #userExists"() { - given: - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setUserDAO userDao - niFiServiceFacade.setControllerFacade controllerFacade - def user = new User.Builder().identifier(userDto.id).identity(userDto.identity).build() - - when: - def userEntity = niFiServiceFacade.deleteUser(currentRevision, userDto.id) - - then: - if (userExists) { - 1 * userDao.getUser(userDto.id) >> user - 1 * userDao.deleteUser(userDto.id) >> user - } else { - 1 * userDao.getUser(userDto.id) >> null - 1 * userDao.deleteUser(userDto.id) >> null - } - 1 * controllerFacade.save() - 1 * revisionManager.deleteRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser nifiUser, DeleteRevisionTask task -> - task.performTask() - } - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.usersResource, - isAuthorized, authorizationResult) - 0 * _ - userEntity?.component?.id == null - if (userExists) { - assert userEntity?.id?.equals(userDto.id) - } else { - assert userEntity?.id == null - } - - where: - userExists | currentRevision | userDto | isAuthorized | authorizationResult - true | new Revision(1L, 'client1', 'user-1') | createUserDTO() | true | AuthorizationResult.approved() - false | null | createUserDTO() | true | AuthorizationResult.approved() - true | new Revision(1L, 'client1', 'user-1') | createUserDTO() | false | AuthorizationResult.denied() - false | null | createUserDTO() | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "CreateUserGroup: isAuthorized: #isAuthorized"() { - given: - def userGroupDao = Mock UserGroupDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def controllerServiceProvider = Mock ControllerServiceProvider - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - dtoFactory.setControllerServiceProvider controllerServiceProvider - dtoFactory.setEntityFactory entityFactory - def authorizableLookup = Mock AuthorizableLookup - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setUserGroupDAO userGroupDao - niFiServiceFacade.setUserDAO userDao - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setControllerFacade controllerFacade - def newUserGroup = new Group.Builder().identifier(userGroupDto.id).name(userGroupDto.name).addUsers(userGroupDto.users.collect { it.id } as Set).build() - def exception = null - def userGroupEntity = null - - when: - try { - userGroupEntity = niFiServiceFacade.createUserGroup(new Revision(0L, 'client-1', userGroupDto.id), userGroupDto) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * authorizableLookup.getUserGroupsAuthorizable() >> - new SimpleAuthorizable(null, ResourceFactory.userGroupsResource, isAuthorized, authorizationResult.get(ResourceFactory.userGroupsResource)) - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.usersResource, isAuthorized, authorizationResult.get(ResourceFactory.usersResource)) - 1 * userGroupDao.createUserGroup(_) >> newUserGroup - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = userGroupDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity) - .addGroups(userEntity.groups.collect { it.getIdentifier } as Set) - .build() - } - userGroupDto.users.size() * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - assert userGroupDto.users.collect { it.id }.contains(id) - def revisionDTO = userGroupDto.users.find { it.id.equals(id) }.revision - callback.withRevision new Revision(revisionDTO.version, revisionDTO.clientId, id) - } - 0 * _ - - assert userGroupEntity != null - if (isAuthorized) { - assert userGroupEntity?.component?.id == userGroupDto.id - assert userGroupEntity?.component?.users?.equals(userGroupDto.users) - assert userGroupEntity?.permissions?.canRead - assert userGroupEntity?.permissions?.canWrite - } else { - assert userGroupEntity?.component == null - } - - - where: // TODO add more use cases, specifically with varied authorization results, and the assertions to check them, to all spec methods that use AuthorizationResult - userGroupDto | isAuthorized | authorizationResult - createUserGroupDTO() | true | [(ResourceFactory.userGroupsResource): AuthorizationResult.approved(), (ResourceFactory.usersResource): AuthorizationResult.approved()] - createUserGroupDTO() | false | [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] - } - - @Ignore - @Unroll - def "GetUserGroup: isAuthorized: #isAuthorized"() { - given: - def userGroupDao = Mock UserGroupDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setUserGroupDAO userGroupDao - niFiServiceFacade.setUserDAO userDao - def requestedUserGroup = new Group.Builder().identifier(userGroupDto.id).name(userGroupDto.name) - .addUsers(userGroupDto.users.collect { it.id } as Set).build() - def exception = null - def userGroupEntity = null - - when: - try { - userGroupEntity = niFiServiceFacade.getUserGroup(userGroupDto.id, true) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * userGroupDao.getUserGroup(userGroupDto.id) >> requestedUserGroup - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.usersResource, isAuthorized, authorizationResult) - 1 * authorizableLookup.getUserGroupsAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUserGroupsResource(), - isAuthorized, authorizationResult) - _ * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = userGroupDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - 0 * _ - - assert userGroupEntity != null - if (isAuthorized) { - assert userGroupEntity?.component?.id?.equals(userGroupDto.id) - } else { - assert userGroupEntity.component == null - } - - where: - userGroupDto | isAuthorized | authorizationResult - new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | true | AuthorizationResult.approved() - new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "UpdateUserGroup: isAuthorized: #isAuthorized, userGroupExists exists: #userGroupExists"() { - given: - def userGroupDao = Mock UserGroupDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - def authorizableLookup = Mock AuthorizableLookup - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setUserGroupDAO userGroupDao - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setControllerFacade controllerFacade - niFiServiceFacade.setUserDAO userDao - def userGroup = new Group.Builder().identifier(userGroupDto.id).name(userGroupDto.name) - .addUsers(userGroupDto.users.collect { it.id } as Set).build() - def userGroupsEntityUpdateResult = null - def exception = null - - when: - try { - userGroupsEntityUpdateResult = niFiServiceFacade.updateUserGroup(currentRevision, userGroupDto) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * userGroupDao.hasUserGroup(userGroupDto.id) >> userGroupExists - if (!userGroupExists) { - 1 * userGroupDao.createUserGroup(userGroupDto) >> userGroup - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult.get(ResourceFactory.getUsersResource())) - } else { - 1 * controllerFacade.save() - 1 * userGroupDao.updateUserGroup(userGroupDto) >> userGroup - 1 * revisionManager.updateRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser niFiUser, UpdateRevisionTask callback -> - callback.update() - } - 1 * revisionManager.getRevision(currentRevision.componentId) >> currentRevision.incrementRevision(currentRevision.clientId) - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.usersResource, - isAuthorized, authorizationResult.get(ResourceFactory.usersResource)) - } - 1 * authorizableLookup.getUserGroupsAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.userGroupsResource, - isAuthorized, authorizationResult.get(ResourceFactory.userGroupsResource)) - _ * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = userGroupDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - 0 * _ - def userGroupEntity = userGroupsEntityUpdateResult?.result - - assert userGroupEntity != null - if (isAuthorized) { - assert userGroupEntity?.component?.getIdentifier?.equals(userGroupDto.id) - assert userGroupEntity?.getPermissions?.canRead - assert userGroupEntity?.getPermissions?.canWrite - } else { - assert userGroupEntity.component == null - } - - where: - userGroupExists | currentRevision | userGroupDto | isAuthorized | - authorizationResult - false | new Revision(0L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | true | - [(ResourceFactory.userGroupsResource): AuthorizationResult.approved(), (ResourceFactory.usersResource): AuthorizationResult.approved()] - true | new Revision(1L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | true | - [(ResourceFactory.userGroupsResource): AuthorizationResult.approved(), (ResourceFactory.usersResource): AuthorizationResult.approved()] - false | new Revision(0L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | - [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] - true | new Revision(1L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | - [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] - } - - @Ignore - @Unroll - def "DeleteUserGroup: isAuthorized: #isAuthorized, userGroup exists: #userGroupExists"() { - given: - def userGroupDao = Mock UserGroupDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setUserGroupDAO userGroupDao - niFiServiceFacade.setControllerFacade controllerFacade - niFiServiceFacade.setUserDAO userDao - def userGroup = new Group.Builder().identifier(userGroupDto.id).name(userGroupDto.name) - .addUsers(userGroupDto.users.collect { it.id } as Set).build() - def userGroupEntity = null - def exception = null - - when: - try { - userGroupEntity = niFiServiceFacade.deleteUserGroup(currentRevision, userGroupDto.id) - } catch (AccessDeniedException e) { - exception = e - } - - then: - if (userGroupExists) { - 1 * userGroupDao.getUserGroup(userGroupDto.id) >> userGroup - 1 * userGroupDao.deleteUserGroup(userGroupDto.id) >> userGroup - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult.get(ResourceFactory.getUsersResource())) - } else { - 1 * userGroupDao.getUserGroup(userGroupDto.id) >> null - 1 * userGroupDao.deleteUserGroup(userGroupDto.id) >> null - } - 1 * authorizableLookup.getUserGroupsAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.userGroupsResource, - isAuthorized, authorizationResult.get(ResourceFactory.userGroupsResource)) - 1 * revisionManager.deleteRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser nifiUser, DeleteRevisionTask task -> - task.performTask() - } - 1 * controllerFacade.save() - _ * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - if (userGroupExists) { - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = userGroupDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - } - 0 * _ - userGroupEntity?.component?.id == null - if (userGroupExists) { - assert userGroupEntity?.id?.equals(userGroupDto.id) - } else { - assert userGroupEntity?.id == null - } - - where: - userGroupExists | currentRevision | userGroupDto | isAuthorized | - authorizationResult - true | new Revision(1L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | true | - [(ResourceFactory.userGroupsResource): AuthorizationResult.approved(), (ResourceFactory.usersResource): AuthorizationResult.approved()] - false | null | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | true | - [(ResourceFactory.userGroupsResource): AuthorizationResult.approved(), (ResourceFactory.usersResource): AuthorizationResult.approved()] - true | new Revision(1L, 'client1', 'root') | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | - [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] - false | null | new UserGroupDTO(id: '1', name: 'test group', users: [createUserEntity()]) | false | - [(ResourceFactory.userGroupsResource): AuthorizationResult.denied(), (ResourceFactory.usersResource): AuthorizationResult.denied()] - } - - @Ignore - @Unroll - def "CreateAccessPolicy: #isAuthorized"() { - given: - def accessPolicyDao = Mock AccessPolicyDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - dtoFactory.setEntityFactory entityFactory - def authorizableLookup = Mock AuthorizableLookup - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setAccessPolicyDAO accessPolicyDao - niFiServiceFacade.setUserDAO userDao - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setRevisionManager revisionManager - def builder = new AccessPolicy.Builder().identifier(accessPolicyDto.id).resource(accessPolicyDto.resource) - .addUsers(accessPolicyDto.users.collect { it.id } as Set) - .addGroups(accessPolicyDto.userGroups.collect { it.id } as Set) - if (accessPolicyDto.canRead) { - builder.addAction(RequestAction.READ) - } - if (accessPolicyDto.canWrite) { - builder.addAction(RequestAction.WRITE) - } - def newAccessPolicy = builder.build() - def accessPolicyEntity = null - def exception = null - - when: - try { - accessPolicyEntity = niFiServiceFacade.createAccessPolicy(new Revision(0L, 'client-1', accessPolicyDto.id), accessPolicyDto) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * accessPolicyDao.createAccessPolicy(accessPolicyDto) >> newAccessPolicy - 1 * authorizableLookup.getAccessPolicyAuthorizable(accessPolicyDto.id) >> new SimpleAuthorizable(null, ResourceFactory.getPolicyResource(accessPolicyDto.id), - isAuthorized, authorizationResult) - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult) - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = accessPolicyDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - 1 * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 0 * _ - - assert accessPolicyEntity != null - if (isAuthorized) { - assert accessPolicyEntity?.component?.id?.equals(accessPolicyDto.id) - assert accessPolicyEntity?.permissions?.canRead - assert accessPolicyEntity?.permissions?.canWrite - } else { - assert accessPolicyEntity.component == null - } - - where: - accessPolicyDto | isAuthorized | authorizationResult - new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | AuthorizationResult.approved() - new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "GetAccessPolicy: isAuthorized: #isAuthorized"() { - given: - def accessPolicyDao = Mock AccessPolicyDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setAccessPolicyDAO accessPolicyDao - niFiServiceFacade.setUserDAO userDao - def builder = new AccessPolicy.Builder().identifier(accessPolicyDto.id).resource(accessPolicyDto.resource) - .addUsers(accessPolicyDto.users.collect { it.id } as Set) - .addGroups(accessPolicyDto.userGroups.collect { it.id } as Set) - if (accessPolicyDto.canRead) { - builder.addAction(RequestAction.READ) - } - if (accessPolicyDto.canWrite) { - builder.addAction(RequestAction.WRITE) - } - def requestedAccessPolicy = builder.build() - def exception = null - def accessPolicyEntity = null - - when: - try { - accessPolicyEntity = niFiServiceFacade.getAccessPolicy(accessPolicyDto.id) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * accessPolicyDao.getAccessPolicy(accessPolicyDto.id) >> requestedAccessPolicy - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult) - _ * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 1 * authorizableLookup.getAccessPolicyAuthorizable(accessPolicyDto.id) >> new SimpleAuthorizable(null, ResourceFactory.getPolicyResource(accessPolicyDto.id), - isAuthorized, authorizationResult) - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = accessPolicyDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - 0 * _ - - assert accessPolicyEntity != null - if (isAuthorized) { - assert accessPolicyEntity?.component?.id?.equals(accessPolicyDto.id) - } else { - assert accessPolicyEntity.component == null - } - - where: - accessPolicyDto | isAuthorized | authorizationResult - new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | AuthorizationResult.approved() - new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "UpdateAccessPolicy: isAuthorized: #isAuthorized, policy exists: #hasPolicy"() { - given: - def accessPolicyDao = Mock AccessPolicyDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - def authorizableLookup = Mock AuthorizableLookup - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setAccessPolicyDAO accessPolicyDao - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setControllerFacade controllerFacade - niFiServiceFacade.setUserDAO userDao - def builder = new AccessPolicy.Builder().identifier(accessPolicyDto.id).resource(accessPolicyDto.resource) - .addUsers(accessPolicyDto.users.collect { it.id } as Set) - .addGroups(accessPolicyDto.userGroups.collect { it.id } as Set) - if (accessPolicyDto.canRead) { - builder.addAction(RequestAction.READ) - } - if (accessPolicyDto.canWrite) { - builder.addAction(RequestAction.WRITE) - } - def accessPolicy = builder.build() - def accessPolicyEntityUpdateResult = null - def exception = null - - when: - try { - accessPolicyEntityUpdateResult = niFiServiceFacade.updateAccessPolicy(currentRevision, accessPolicyDto) - } catch (AccessDeniedException e) { - exception = e - } - - then: - 1 * accessPolicyDao.hasAccessPolicy(accessPolicyDto.id) >> hasPolicy - if (!hasPolicy) { - 1 * accessPolicyDao.createAccessPolicy(accessPolicyDto) >> accessPolicy - } else { - 1 * controllerFacade.save() - 1 * accessPolicyDao.updateAccessPolicy(accessPolicyDto) >> accessPolicy - 1 * revisionManager.updateRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser niFiUser, UpdateRevisionTask callback -> - callback.update() - } - 1 * revisionManager.getRevision(currentRevision.componentId) >> currentRevision.incrementRevision(currentRevision.clientId) - } - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.getUsersResource(), - isAuthorized, authorizationResult) - 1 * authorizableLookup.getAccessPolicyAuthorizable(accessPolicyDto.id) >> new SimpleAuthorizable(null, ResourceFactory.getPolicyResource(accessPolicyDto.id), - isAuthorized, authorizationResult) - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = accessPolicyDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - 1 * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - 0 * _ - def accessPolicyEntity = accessPolicyEntityUpdateResult?.result - - assert accessPolicyEntity != null - if (isAuthorized) { - assert accessPolicyEntity?.component?.getIdentifier?.equals(accessPolicyDto.id) - assert accessPolicyEntity?.getPermissions?.canRead - assert accessPolicyEntity?.getPermissions?.canWrite - } else { - assert accessPolicyEntity.component == null - } - - where: - hasPolicy | currentRevision | accessPolicyDto | isAuthorized | - authorizationResult - false | new Revision(0L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | - AuthorizationResult.approved() - true | new Revision(1L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | - AuthorizationResult.approved() - false | new Revision(0L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | - AuthorizationResult.denied() - true | new Revision(1L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | - AuthorizationResult.denied() - } - - @Ignore - @Unroll - def "DeleteAccessPolicy: isAuthorized: #isAuthorized, hasPolicy: #hasPolicy"() { - given: - def accessPolicyDao = Mock AccessPolicyDAO - def userDao = Mock UserDAO - def revisionManager = Mock RevisionManager - def authorizableLookup = Mock AuthorizableLookup - def dtoFactory = new DtoFactory() - def entityFactory = new EntityFactory() - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setRevisionManager revisionManager - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setAccessPolicyDAO accessPolicyDao - niFiServiceFacade.setControllerFacade controllerFacade - niFiServiceFacade.setUserDAO userDao - def builder = new AccessPolicy.Builder() - builder.identifier(accessPolicyDto.id).resource(accessPolicyDto.resource) - .addUsers(accessPolicyDto.users.collect { it.id } as Set) - .addGroups(accessPolicyDto.userGroups.collect { it.id } as Set) - if (accessPolicyDto.canRead) { - builder.addAction(RequestAction.READ) - } - if (accessPolicyDto.canWrite) { - builder.addAction(RequestAction.WRITE) - } - def accessPolicy = builder.build() - def accessPolicyEntity = null - def exception = null - - when: - try { - accessPolicyEntity = niFiServiceFacade.deleteAccessPolicy(currentRevision, accessPolicyDto.id) - } catch (AccessDeniedException e) { - exception = e - } - - then: - if (hasPolicy) { - 1 * accessPolicyDao.getAccessPolicy(accessPolicyDto.id) >> accessPolicy - 1 * accessPolicyDao.deleteAccessPolicy(accessPolicyDto.id) >> accessPolicy - 1 * authorizableLookup.getUsersAuthorizable() >> new SimpleAuthorizable(null, ResourceFactory.usersResource, - isAuthorized, authorizationResult) - } else { - 1 * accessPolicyDao.getAccessPolicy(accessPolicyDto.id) >> null - 1 * accessPolicyDao.deleteAccessPolicy(accessPolicyDto.id) >> null - } - 1 * authorizableLookup.getAccessPolicyAuthorizable(accessPolicyDto.id) >> new SimpleAuthorizable(null, ResourceFactory.getPolicyResource(accessPolicyDto.id), - isAuthorized, authorizationResult) - 1 * revisionManager.deleteRevision(_, _, _) >> { RevisionClaim revisionClaim, NiFiUser nifiUser, DeleteRevisionTask task -> - task.performTask() - } - 1 * controllerFacade.save() - _ * revisionManager.get(_, _) >> { String id, ReadOnlyRevisionCallback callback -> - callback.withRevision(new Revision(1L, 'client1', 'root')) - } - if (hasPolicy) { - 1 * userDao.getUser(_) >> { String userId -> - def userEntity = accessPolicyDto.users.find { it.id.equals(userId) }?.component - assert userEntity != null - new User.Builder().identifier(userEntity.id).identity(userEntity.identity).build() - } - } - 0 * _ - - assert accessPolicyEntity != null - if (hasPolicy) { - assert accessPolicyEntity?.id?.equals(accessPolicyDto.id) - } else { - assert accessPolicyEntity?.id == null - } - - where: - hasPolicy | currentRevision | accessPolicyDto | isAuthorized | - authorizationResult - true | new Revision(1L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | - AuthorizationResult.approved() - false | null | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | true | - AuthorizationResult.approved() - true | new Revision(1L, 'client1', 'root') | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | - AuthorizationResult.denied() - false | null | new AccessPolicyDTO(id: '1', resource: ResourceFactory.flowResource.identifier, users: [createUserEntity()], canRead: true) | false | - AuthorizationResult.denied() - } - - - def "CreateBulletin Successfully"() { - given: - - def entityFactory = new EntityFactory() - def dtoFactory = new DtoFactory() - dtoFactory.setEntityFactory entityFactory - def authorizableLookup = Mock AuthorizableLookup - def controllerFacade = Mock ControllerFacade - def niFiServiceFacade = new StandardNiFiServiceFacade() - def bulletinRepository = Mock BulletinRepository - niFiServiceFacade.setAuthorizableLookup authorizableLookup - niFiServiceFacade.setDtoFactory dtoFactory - niFiServiceFacade.setEntityFactory entityFactory - niFiServiceFacade.setControllerFacade controllerFacade - niFiServiceFacade.setBulletinRepository bulletinRepository - - def bulletinDto = new BulletinDTO() - bulletinDto.category = "SYSTEM" - bulletinDto.message = "test system message" - bulletinDto.level = "WARN" - def bulletinEntity - def retBulletinEntity = new BulletinEntity() - retBulletinEntity.bulletin = bulletinDto - - when: - - bulletinEntity = niFiServiceFacade.createBulletin(bulletinDto,true) - - - then: - 1 * bulletinRepository.addBulletin(_ as Bulletin) - bulletinEntity - bulletinEntity.bulletin.message == bulletinDto.message - - - } - - def "Test REST API Prometheus Metrics Endpoint"() { - given: - def serviceFacade = new StandardNiFiServiceFacade() - BulletinRepository bulletinRepository = new MockBulletinRepository() - serviceFacade.setBulletinRepository(bulletinRepository) - - ControllerFacade controllerFacade = Mock() - serviceFacade.setControllerFacade(controllerFacade) - controllerFacade.getInstanceId() >> "ABC" - controllerFacade.getMaxEventDrivenThreadCount() >> 1 - controllerFacade.getMaxTimerDrivenThreadCount() >> 10 - - // Setting up storage repositories - StorageUsage flowFileStorage = new StorageUsage() - flowFileStorage.setIdentifier("flowFile") - flowFileStorage.setTotalSpace(222) - flowFileStorage.setFreeSpace(111) - - StorageUsage contentStorage = new StorageUsage() - contentStorage.setIdentifier("default") - contentStorage.setTotalSpace(444) - contentStorage.setFreeSpace(111) - Map<String, StorageUsage> contentStorageMap = new HashMap<>() - contentStorageMap.put("default", contentStorage) - - StorageUsage provenanceStorage = new StorageUsage() - provenanceStorage.setIdentifier("default") - provenanceStorage.setTotalSpace(666) - provenanceStorage.setFreeSpace(111) - Map<String, StorageUsage> provenanceStorageMap = new HashMap<>() - provenanceStorageMap.put("default", provenanceStorage) - - // Setting up SystemDiagnostics - SystemDiagnostics systemDiagnostics = new SystemDiagnostics() - systemDiagnostics.setFlowFileRepositoryStorageUsage(flowFileStorage) - systemDiagnostics.setContentRepositoryStorageUsage(contentStorageMap) - systemDiagnostics.setProvenanceRepositoryStorageUsage(provenanceStorageMap) - - controllerFacade.getSystemDiagnostics() >> systemDiagnostics - - // Setting up flow - ProcessGroupStatus rootGroupStatus = new ProcessGroupStatus() - rootGroupStatus.setId("1234"); - rootGroupStatus.setFlowFilesReceived(5); - rootGroupStatus.setBytesReceived(10000); - rootGroupStatus.setFlowFilesSent(10); - rootGroupStatus.setBytesSent(20000); - rootGroupStatus.setQueuedCount(100); - rootGroupStatus.setQueuedContentSize(1024L); - rootGroupStatus.setBytesRead(60000L); - rootGroupStatus.setBytesWritten(80000L); - rootGroupStatus.setActiveThreadCount(5); - rootGroupStatus.setName("root"); - rootGroupStatus.setFlowFilesTransferred(5); - rootGroupStatus.setBytesTransferred(10000); - rootGroupStatus.setOutputContentSize(1000L); - rootGroupStatus.setInputContentSize(1000L); - rootGroupStatus.setOutputCount(100); - rootGroupStatus.setInputCount(1000); - - PortStatus outputPortStatus = new PortStatus(); - outputPortStatus.setId("9876"); - outputPortStatus.setName("out"); - outputPortStatus.setGroupId("1234"); - outputPortStatus.setRunStatus(RunStatus.Stopped); - outputPortStatus.setActiveThreadCount(1); - - rootGroupStatus.setOutputPortStatus(Collections.singletonList(outputPortStatus)); - // Create a nested group status - ProcessGroupStatus groupStatus2 = new ProcessGroupStatus(); - groupStatus2.setFlowFilesReceived(5); - groupStatus2.setBytesReceived(10000); - groupStatus2.setFlowFilesSent(10); - groupStatus2.setBytesSent(20000); - groupStatus2.setQueuedCount(100); - groupStatus2.setQueuedContentSize(1024L); - groupStatus2.setActiveThreadCount(2); - groupStatus2.setBytesRead(12345L); - groupStatus2.setBytesWritten(11111L); - groupStatus2.setFlowFilesTransferred(5); - groupStatus2.setBytesTransferred(10000); - groupStatus2.setOutputContentSize(1000L); - groupStatus2.setInputContentSize(1000L); - groupStatus2.setOutputCount(100); - groupStatus2.setInputCount(1000); - groupStatus2.setId("3378"); - groupStatus2.setName("nestedPG"); - Collection<ProcessGroupStatus> nestedGroupStatuses = new ArrayList<>(); - nestedGroupStatuses.add(groupStatus2); - rootGroupStatus.setProcessGroupStatus(nestedGroupStatuses); - - // setting up flowFile events - controllerFacade.getProcessGroupStatus("root") >> rootGroupStatus - FlowFileEventRepository flowFileEventRepository = Mock() - controllerFacade.getFlowFileEventRepository() >> flowFileEventRepository - FlowFileEvent aggregateEvent = Mock() - flowFileEventRepository.reportAggregateEvent() >> aggregateEvent - - ProcessGroupDAO processGroupDAO = Mock() - serviceFacade.setProcessGroupDAO(processGroupDAO) - ProcessGroup processGroup = Mock() - processGroupDAO.getProcessGroup(rootGroupStatus.getId()) >> processGroup - DtoFactory dtoFactory = new DtoFactory() - serviceFacade.setDtoFactory(dtoFactory) - PermissionsDTO permissions = Mock() - dtoFactory.createPermissionsDto(processGroup) >> permissions - StatusHistoryEntity statusHistoryEntity = new StatusHistoryEntity() - StatusHistoryDTO statusHistory = new StatusHistoryDTO() - statusHistory.setAggregateSnapshots(Collections.EMPTY_LIST) - statusHistoryEntity.setStatusHistory(statusHistory) - controllerFacade.getProcessGroupStatusHistory("1234") >> statusHistory - EntityFactory entityFactory = new EntityFactory() - serviceFacade.setEntityFactory(entityFactory) - entityFactory.createStatusHistoryEntity(statusHistoryEntity, permissions) >> statusHistoryEntity - serviceFacade.getProcessGroupStatusHistory("1234") >> statusHistory - - // setting up connections (empty list for testing) - Set<Connection> connections = new HashSet() - StandardFlowManager flowManager = Mock() - controllerFacade.getFlowManager() >> flowManager - flowManager.findAllConnections() >> connections - - when: - Collection<CollectorRegistry> allRegistries = serviceFacade.generateFlowMetrics() - - // Converts metrics into a String for testing - Writer writer = new StringWriter(); - for (CollectorRegistry collectorRegistry : allRegistries) { - TextFormat.write004(writer, collectorRegistry.metricFamilySamples()); - } - String output = writer.toString(); - writer.close() - - // rename root group and generate metrics again to a different string - rootGroupStatus.setName("rootroot") - allRegistries = serviceFacade.generateFlowMetrics() - writer = new StringWriter() - for (CollectorRegistry collectorRegistry : allRegistries) { - TextFormat.write004(writer, collectorRegistry.metricFamilySamples()) - } - String output2 = writer.toString() - writer.close() - - then: - // flow metrics - output.contains("nifi_amount_flowfiles_received{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"root\",component_id=\"1234\",parent_id=\"\",} 5.0"); - output.contains("nifi_amount_threads_active{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"root\",component_id=\"1234\",parent_id=\"\",} 5.0"); - output.contains("nifi_amount_threads_active{instance=\"ABC\",component_type=\"ProcessGroup\",component_name=\"nestedPG\",component_id=\"3378\",parent_id=\"1234\",} 2.0"); - - // jvm - output.contains("nifi_jvm_heap_used{instance=\"ABC\",}") - output.contains("# HELP nifi_jvm_heap_used NiFi JVM heap used") - output.contains("# TYPE nifi_jvm_heap_used gauge") - output.contains("nifi_jvm_thread_count{instance=\"ABC\",}") - - // test that renamed items are in the metrics output and that the previously named versions have been removed from the metrics output. - output2.contains("nifi_amount_flowfiles_received{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"rootroot\",component_id=\"1234\",parent_id=\"\",} 5.0"); - output2.contains("nifi_amount_threads_active{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"rootroot\",component_id=\"1234\",parent_id=\"\",} 5.0"); - !output2.contains("nifi_amount_flowfiles_received{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"root\",component_id=\"1234\",parent_id=\"\",} 5.0"); - !output2.contains("nifi_amount_threads_active{instance=\"ABC\",component_type=\"RootProcessGroup\",component_name=\"root\",component_id=\"1234\",parent_id=\"\",} 5.0"); - - } - - private UserGroupDTO createUserGroupDTO() { - new UserGroupDTO(id: 'group-1', name: 'test group', users: [createUserEntity()] as Set) - } - - private UserEntity createUserEntity() { - new UserEntity(id: 'user-1', component: createUserDTO(), revision: createRevisionDTO()) - } - - private UserDTO createUserDTO() { - new UserDTO(id: 'user-1', identity: 'user-1') - } - - private RevisionDTO createRevisionDTO() { - new RevisionDTO(version: 0L, clientId: 'client-1', lastModifier: 'user-1') - } - - private class SimpleAuthorizable implements Authorizable { - final private Authorizable parentAuthorizable - final private Resource resource - final private boolean isAuthorized - final private AuthorizationResult authorizationResult; - - SimpleAuthorizable(Authorizable parentAuthorizable, Resource resource, boolean isAuthorized, AuthorizationResult authorizationResult) { - this.parentAuthorizable = parentAuthorizable - this.resource = resource - this.isAuthorized = isAuthorized - this.authorizationResult = authorizationResult - } - - @Override - Authorizable getParentAuthorizable() { - return parentAuthorizable - } - - @Override - Resource getResource() { - return resource - } - - @Override - boolean isAuthorized(Authorizer authorzr, RequestAction action, NiFiUser user) { - return isAuthorized - } - - @Override - AuthorizationResult checkAuthorization(Authorizer authorzr, RequestAction action, NiFiUser user) { - return authorizationResult - } - - @Override - void authorize(Authorizer authorzr, RequestAction action, NiFiUser user) throws AccessDeniedException { - if (!isAuthorized) { - throw new AccessDeniedException("test exception, access denied") - } - } - } -} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy deleted file mode 100644 index 1b67778a88..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ApplicationResourceTest.groovy +++ /dev/null @@ -1,176 +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 - -import org.apache.nifi.util.NiFiProperties -import org.glassfish.jersey.uri.internal.JerseyUriBuilder -import org.junit.jupiter.api.Test - -import javax.servlet.http.HttpServletRequest -import javax.ws.rs.core.UriBuilderException -import javax.ws.rs.core.UriInfo - -import static org.apache.nifi.web.util.WebUtils.PROXY_CONTEXT_PATH_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.PROXY_HOST_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.PROXY_PORT_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.PROXY_SCHEME_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.FORWARDED_CONTEXT_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.FORWARDED_HOST_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.FORWARDED_PORT_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.FORWARDED_PREFIX_HTTP_HEADER -import static org.apache.nifi.web.util.WebUtils.FORWARDED_PROTO_HTTP_HEADER - -import static org.junit.jupiter.api.Assertions.assertThrows - -class ApplicationResourceTest { - static final String PROXY_CONTEXT_PATH_PROP = NiFiProperties.WEB_PROXY_CONTEXT_PATH - static final String ALLOWED_PATH = "/some/context/path" - - class MockApplicationResource extends ApplicationResource { - void setHttpServletRequest(HttpServletRequest request) { - super.httpServletRequest = request - } - - void setUriInfo(UriInfo uriInfo) { - super.uriInfo = uriInfo - } - } - - private ApplicationResource buildApplicationResource() { - buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER, FORWARDED_CONTEXT_HTTP_HEADER, PROXY_CONTEXT_PATH_HTTP_HEADER]) - } - - private ApplicationResource buildApplicationResource(List proxyHeaders) { - ApplicationResource resource = new MockApplicationResource() - String headerValue = "" - HttpServletRequest mockRequest = [getHeader: { String k -> - if (proxyHeaders.contains(k)) { - headerValue = ALLOWED_PATH - } else if ([FORWARDED_PORT_HTTP_HEADER, PROXY_PORT_HTTP_HEADER].contains(k)) { - headerValue = "8081" - } else if ([FORWARDED_PROTO_HTTP_HEADER, PROXY_SCHEME_HTTP_HEADER].contains(k)) { - headerValue = "https" - } else if ([PROXY_HOST_HTTP_HEADER, FORWARDED_HOST_HTTP_HEADER].contains(k)) { - headerValue = "nifi.apache.org:8081" - } else { - headerValue = "" - } - headerValue - }, getContextPath: { -> - headerValue - }, getScheme: { -> - "https" - }, getServerPort: { -> - 443 - }] as HttpServletRequest - - UriInfo mockUriInfo = [getBaseUriBuilder: { -> - new JerseyUriBuilder().uri(new URI('https://nifi.apache.org/')) - }] as UriInfo - - resource.setHttpServletRequest(mockRequest) - resource.setUriInfo(mockUriInfo) - resource.properties = new NiFiProperties() - - resource - } - - @Test - void testGenerateUriShouldBlockProxyContextPathHeaderIfNotInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource() - assertThrows(UriBuilderException.class, () -> resource.generateResourceUri('actualResource')) - } - - @Test - void testGenerateUriShouldAllowProxyContextPathHeaderIfInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource() - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } - - @Test - void testGenerateUriShouldAllowProxyContextPathHeaderIfElementInMultipleAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource() - String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",") - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } - - @Test - void testGenerateUriShouldBlockForwardedContextHeaderIfNotInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_CONTEXT_HTTP_HEADER]) - - assertThrows(UriBuilderException.class, () -> resource.generateResourceUri('actualResource')) - } - - @Test - void testGenerateUriShouldBlockForwardedPrefixHeaderIfNotInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER]) - - assertThrows(UriBuilderException.class, () -> resource.generateResourceUri('actualResource')) - } - - @Test - void testGenerateUriShouldAllowForwardedContextHeaderIfInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_CONTEXT_HTTP_HEADER]) - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } - - @Test - void testGenerateUriShouldAllowForwardedPrefixHeaderIfInAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER]) - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): ALLOWED_PATH] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } - - @Test - void testGenerateUriShouldAllowForwardedContextHeaderIfElementInMultipleAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_CONTEXT_HTTP_HEADER]) - String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",") - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } - - @Test - void testGenerateUriShouldAllowForwardedPrefixHeaderIfElementInMultipleAllowList() throws Exception { - ApplicationResource resource = buildApplicationResource([FORWARDED_PREFIX_HTTP_HEADER]) - String multipleAllowedPaths = [ALLOWED_PATH, "another/path", "a/third/path"].join(",") - NiFiProperties niFiProperties = new NiFiProperties([(PROXY_CONTEXT_PATH_PROP): multipleAllowedPaths] as Properties) - resource.properties = niFiProperties - - String generatedUri = resource.generateResourceUri('actualResource') - assert generatedUri == "https://nifi.apache.org:8081${ALLOWED_PATH}/actualResource" - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ProcessGroupResourceTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ProcessGroupResourceTest.groovy deleted file mode 100644 index cde4c7bf34..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/ProcessGroupResourceTest.groovy +++ /dev/null @@ -1,154 +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 - -import org.apache.nifi.authorization.AuthorizeAccess -import org.apache.nifi.util.NiFiProperties -import org.apache.nifi.web.NiFiServiceFacade -import org.apache.nifi.web.api.dto.FlowSnippetDTO -import org.apache.nifi.web.api.dto.TemplateDTO -import org.apache.nifi.web.api.entity.TemplateEntity -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -import javax.servlet.http.HttpServletRequest -import javax.ws.rs.core.Response -import javax.ws.rs.core.UriInfo - -class ProcessGroupResourceTest { - private static final Logger logger = LoggerFactory.getLogger(ProcessGroupResourceTest.class) - - @BeforeAll - static void setUpOnce() throws Exception { - logger.metaClass.methodMissing = { String name, args -> - logger.debug("[${name?.toUpperCase()}] ${(args as List).join(" ")}") - } - } - - /** This test creates a malformed template upload request to exercise error handling and sanitization */ - @Test - void testUploadShouldHandleMalformedTemplate() { - // Arrange - ProcessGroupResource pgResource = new ProcessGroupResource() - - // Mocking the returned template object to throw a specific exception would be nice - final String TEMPLATE_WITH_XSS_PLAIN = "<?xml version=\"1.0\" encoding='><script xmlns=\"http://www.w3.org/1999/xhtml\">alert(JSON.stringify(localstorage));</script><errorResponse test='?>" - logger.info("Malformed template XML: ${TEMPLATE_WITH_XSS_PLAIN}") - InputStream contentInputStream = new ByteArrayInputStream(TEMPLATE_WITH_XSS_PLAIN.bytes) - - HttpServletRequest mockRequest = [:] as HttpServletRequest - UriInfo mockUriInfo = [:] as UriInfo - String groupId = "1" - - // Build a malformed template object which can be unmarshalled from XML - - // Act - - // Try to submit the malformed template - Response response = pgResource.uploadTemplate(mockRequest, mockUriInfo, groupId, false, contentInputStream) - logger.info("Response: ${response}") - - // Assert - - // Assert that the expected error response was returned - assert response.status == Response.Status.OK.statusCode - - // Assert that the error response is sanitized - String responseEntity = response.entity as String - logger.info("Error response: ${responseEntity}") - assert !(responseEntity =~ /<script.*>/) - } - - /** This test creates a malformed template import request to exercise error handling and sanitization */ - @Test - void testImportShouldHandleMalformedTemplate() { - // Arrange - ProcessGroupResource pgResource = new ProcessGroupResource() - - // Configure parent fields for write lock process - pgResource.properties = [isNode: { -> return false }] as NiFiProperties - pgResource.serviceFacade = [ - authorizeAccess : { AuthorizeAccess a -> }, - verifyCanAddTemplate: { String gid, String templateName -> }, - importTemplate : { TemplateDTO template, String gid, Optional<String> seedId -> - logger.mock("Called importTemplate;") - template - } - ] as NiFiServiceFacade - pgResource.templateResource = [ - populateRemainingTemplateContent: { TemplateDTO td -> } - ] as TemplateResource - - final String TEMPLATE_WITH_XSS_PLAIN = "<?xml version=\"1.0\" encoding='><script xmlns=\"http://www.w3.org/1999/xhtml\">alert(JSON.stringify(localstorage));</script><errorResponse test='?>" - logger.info("Malformed template XML: ${TEMPLATE_WITH_XSS_PLAIN}") - - TemplateDTO mockIAETemplate = [ - getName : { -> "mockIAETemplate" }, - getUri : { -> - throw new IllegalArgumentException("Expected exception with <script> element") - }, - getSnippet: { -> new FlowSnippetDTO() } - ] as TemplateDTO - - TemplateDTO mockExceptionTemplate = [ - getName : { -> "mockExceptionTemplate" }, - getUri : { -> - throw new RuntimeException("Expected exception with <script> element") - }, - getSnippet: { -> new FlowSnippetDTO() } - ] as TemplateDTO - - TemplateEntity mockIAETemplateEntity = [getTemplate: { -> - mockIAETemplate - }] as TemplateEntity - - TemplateEntity mockExceptionTemplateEntity = [getTemplate: { -> - mockExceptionTemplate - }] as TemplateEntity - - // Override the request object and store it for ApplicationResource#withWriteLock - HttpServletRequest mockRequest = [getHeader: { String headerName -> - logger.mock("Requesting header ${headerName}; returning null") - null - }] as HttpServletRequest - - // Set the persisted request object so the parent ApplicationResource can use it - pgResource.httpServletRequest = mockRequest - String groupId = "1" - - // Act - List<Response> responses = [mockIAETemplateEntity, mockExceptionTemplateEntity].collect { TemplateEntity te -> - // Try to submit the malformed template which throws some kind of exception - Response response = pgResource.importTemplate(mockRequest, groupId, te) - logger.info("Response: ${response}") - response - } - - // Assert - responses.each { Response r -> - // Assert that the expected error response was returned - assert r.status == Response.Status.OK.statusCode - - // Assert that the error response is sanitized - String entity = r.entity as String - logger.info("Error response: ${entity}") - assert !(entity =~ /<script.*>/) - } - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.groovy deleted file mode 100644 index d871c8fde1..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.groovy +++ /dev/null @@ -1,86 +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.config - -import com.fasterxml.jackson.core.JsonLocation -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.JsonMappingException -import com.fasterxml.jackson.databind.exc.InvalidFormatException -import org.apache.nifi.web.api.ProcessGroupResourceTest -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -import javax.ws.rs.core.Response - -class JsonContentConversionExceptionMapperTest extends GroovyTestCase { - private static final Logger logger = LoggerFactory.getLogger(ProcessGroupResourceTest.class) - - @BeforeAll - static void setUpOnce() throws Exception { - logger.metaClass.methodMissing = { String name, args -> - logger.debug("[${name?.toUpperCase()}] ${(args as List).join(" ")}") - } - } - - @Test - void testShouldThrowExceptionWithStringPortValue() throws Exception{ - // Arrange - JsonContentConversionExceptionMapper jsonCCEM = new JsonContentConversionExceptionMapper() - - // Using real exception - Class<?> instClass = Integer.class - def mockParser = [getTokenLocation: { -> return new JsonLocation(null, 100, 1, 1)}] as JsonParser - String message = "Some message" - String value = "thisIsAnInvalidPort" - InvalidFormatException ife = InvalidFormatException.from(mockParser, message, value, instClass) - JsonMappingException.wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupDTO", "proxyPort")) - JsonMappingException.wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupEntity", "component")) - - // Act - Response response = jsonCCEM.toResponse(ife) - logger.info(response.toString()) - - // Assert - assert response.status == Response.Status.BAD_REQUEST.statusCode - assert response.entity == "The provided proxyPort value \'thisIsAnInvalidPort\' is not of required type class java.lang.Integer" - } - - @Test - void testShouldSanitizeScriptInInput() throws Exception{ - // Arrange - JsonContentConversionExceptionMapper jsonCCEM = new JsonContentConversionExceptionMapper(); - - // Using real exception - Class<?> instClass = Integer.class - def mockParser = [getTokenLocation: { -> return new JsonLocation(null, 100, 1, 1)}] as JsonParser - String message = "Some message" - String value = "<script>alert(1);</script>" - InvalidFormatException ife = InvalidFormatException.from(mockParser, message, value, instClass) - JsonMappingException.wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupDTO", "proxyPort")) - JsonMappingException.wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupEntity", "component")) - - // Act - Response response = jsonCCEM.toResponse(ife) - logger.info(response.toString()) - - // Assert - assert response.status == Response.Status.BAD_REQUEST.statusCode - assert !(response.entity =~ /<script.*>/) - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy deleted file mode 100644 index 13cd90d2e6..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardPolicyBasedAuthorizerDAOSpec.groovy +++ /dev/null @@ -1,618 +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.dao.impl - -import org.apache.nifi.authorization.* -import org.apache.nifi.web.ResourceNotFoundException -import org.apache.nifi.web.api.dto.AccessPolicyDTO -import org.apache.nifi.web.api.dto.UserDTO -import org.apache.nifi.web.api.dto.UserGroupDTO -import org.apache.nifi.web.api.entity.TenantEntity -import spock.lang.Specification -import spock.lang.Unroll - -class StandardPolicyBasedAuthorizerDAOSpec extends Specification { - - private AbstractPolicyBasedAuthorizer mockAuthorizer() { - def authorizer = Mock AbstractPolicyBasedAuthorizer - authorizer.getAccessPolicyProvider() >> { - callRealMethod(); - } - return authorizer; - } - - @Unroll - def "test non-policy-based authorizer #method throws IllegalStateException"() { - when: - daoMethod() - - then: - def e = thrown(IllegalStateException) - assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_MANAGED_AUTHORIZER) - - where: - method | daoMethod - 'getAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getAccessPolicy('1') } - 'getUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUser('1') } - 'getUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUserGroup('1') } - 'hasAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasAccessPolicy('1') } - 'hasUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUser('1') } - 'hasUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUserGroup('1') } - } - - @Unroll - def "test non-configurable user group provider #method throws IllegalStateException"() { - when: - daoMethod() - - then: - def e = thrown(IllegalStateException) - assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_CONFIGURABLE_USERS) - - where: - method | daoMethod - 'createUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUser(new UserDTO(id: '1', identity: 'a')) } - 'createUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUserGroup(new UserGroupDTO(id: '1', identity: 'a')) } - 'deleteUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUser('1') } - 'deleteUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUserGroup('1') } - 'updateUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUser(new UserDTO(id: '1', identity: 'a')) } - 'updateUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUserGroup(new UserGroupDTO(id: '1', identity: 'a')) } - } - - @Unroll - def "test non-configurable access policy provider #method throws IllegalStateException"() { - when: - daoMethod() - - then: - def e = thrown(IllegalStateException) - assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_CONFIGURABLE_POLICIES) - - where: - method | daoMethod - 'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) } - 'deleteAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteAccessPolicy('1') } - 'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) } - } - - @Unroll - def "HasAccessPolicy: accessPolicy: #accessPolicy"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.hasAccessPolicy('policy-id-1') - - then: - 1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy - 0 * _ - result == (accessPolicy != null) - - where: - accessPolicy | _ - new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1') - .action(RequestAction.WRITE).build() | _ - null | _ - } - - @Unroll - def "CreateAccessPolicy: accessPolicy=#accessPolicy"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", - users: [new TenantEntity(id: 'user-id-1')] as Set, - userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - def result = dao.createAccessPolicy(requestDTO) - - then: - noExceptionThrown() - - then: - 1 * authorizer.doAddAccessPolicy(accessPolicy) >> accessPolicy - 0 * _ - result?.equals accessPolicy - - where: - accessPolicy | accessPolicies - new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1') - .action(RequestAction.WRITE).build() | [] as Set - } - - @Unroll - def "GetAccessPolicy: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.getAccessPolicy('policy-id-1') - - then: - 1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy - 0 * _ - assert result?.equals(accessPolicy) - - where: - accessPolicy | _ - new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1') - .action(RequestAction.WRITE).build() | _ - } - - @Unroll - def "GetAccessPoliciesForUser: access policy contains identifier of missing group"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def group1 = new Group.Builder().identifier("group-id-1").name("Group One").addUser("user-id-1").build() - def apBuilder = new AccessPolicy.Builder().resource('/fake/resource').action(RequestAction.WRITE) - def ap1 = apBuilder.identifier('policy-id-1').addUser('user-id-1').build() - def ap2 = apBuilder.identifier('policy-id-2').clearUsers().addGroup('group-id-1').build() - def ap3 = apBuilder.identifier('policy-id-3').clearUsers().clearGroups().addGroup('id-of-missing-group').build() - def accessPolicies = new HashSet([ap1, ap2, ap3]) - - when: - def result = dao.getAccessPoliciesForUser('user-id-1') - - then: - 1 * authorizer.getAccessPolicies() >> accessPolicies - 1 * authorizer.getGroup('group-id-1') >> group1 - 1 * authorizer.getGroup('id-of-missing-group') >> null - 0 * _ - assert result?.equals(new HashSet<AccessPolicy>([ap1, ap2])) - } - - @Unroll - def "GetAccessPolicy: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.getAccessPolicy('policy-id-1') - - then: - 1 * authorizer.getAccessPolicy('policy-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "UpdateAccessPolicy: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", - users: [new TenantEntity(id: 'user-id-1')] as Set, - userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - def result = dao.updateAccessPolicy(requestDTO) - - then: - 1 * authorizer.getAccessPolicy(requestDTO.id) >> accessPolicy - 1 * authorizer.updateAccessPolicy(accessPolicy) >> accessPolicy - 0 * _ - result?.equals(accessPolicy) - - where: - accessPolicy | _ - new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1') - .action(RequestAction.WRITE).build() | _ - } - - @Unroll - def "UpdateAccessPolicy: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read", - users: [new TenantEntity(id: 'user-id-1')] as Set, - userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - dao.updateAccessPolicy(requestDTO) - - then: - 1 * authorizer.getAccessPolicy(requestDTO.id) >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "DeleteAccessPolicy: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.deleteAccessPolicy('policy-id-1') - - then: - 1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy - 1 * authorizer.deleteAccessPolicy(accessPolicy) >> accessPolicy - 0 * _ - result?.equals(accessPolicy) - - where: - accessPolicy | _ - new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1') - .action(RequestAction.WRITE).build() | _ - } - - @Unroll - def "DeleteAccessPolicy: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.deleteAccessPolicy('policy-id-1') - - then: - 1 * authorizer.getAccessPolicy('policy-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "HasUserGroup: userGroup=#userGroup"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.hasUserGroup('user-group-id-1') - - then: - 1 * authorizer.getGroup('user-group-id-1') >> userGroup - 0 * _ - result == (userGroup != null) - - where: - userGroup | _ - new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _ - null | _ - } - - @Unroll - def "CreateUserGroup: userGroup=#userGroup"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set) - - when: - def result = dao.createUserGroup(requestDTO) - - then: - noExceptionThrown() - - then: - 1 * authorizer.doAddGroup(userGroup) >> userGroup - 0 * _ - result?.equals userGroup - - where: - userGroup | users | groups - new Group.Builder().identifier('user-group-id-1') - .name('user-group-id-1').addUser('user-id-1').build() | [] as Set | [] as Set - } - - @Unroll - def "GetUserGroup: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.getUserGroup('user-group-id-1') - - then: - 1 * authorizer.getGroup('user-group-id-1') >> userGroup - 0 * _ - result?.equals(userGroup) - - where: - userGroup | _ - new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _ - } - - @Unroll - def "GetUserGroup: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.getUserGroup('user-group-id-1') - - then: - 1 * authorizer.getGroup('user-group-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "GetUserGroups: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.getUserGroups() - - then: - 1 * authorizer.getGroups() >> userGroups - 0 * _ - result?.equals(userGroups) - - where: - userGroups | _ - [new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build()] as Set | _ - } - - @Unroll - def "UpdateUserGroup: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set) - - when: - def result = dao.updateUserGroup(requestDTO) - - then: - 1 * authorizer.getGroup(requestDTO.id) >> userGroup - 1 * authorizer.doUpdateGroup(userGroup) >> userGroup - 0 * _ - result?.equals(userGroup) - - where: - userGroup | users | groups - new Group.Builder().identifier('user-group-id-1') - .name('user-group-id-1').addUser('user-id-1').build() | [] as Set | [] as Set - } - - @Unroll - def "UpdateUserGroup: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set) - - when: - dao.updateUserGroup(requestDTO) - - then: - 1 * authorizer.getGroup(requestDTO.id) >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "DeleteUserGroup: success"() { - given: - def authorizer = mockAuthorizer() - authorizer.getAccessPolicyProvider().getAccessPolicies() >> { - callRealMethod(); - } - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.deleteUserGroup('user-group-id-1') - - then: - 1 * authorizer.getGroup('user-group-id-1') >> userGroup - 1 * authorizer.deleteGroup(userGroup) >> userGroup - 1 * authorizer.getAccessPolicies() >> [] - 0 * _ - assert result?.equals(userGroup) - - where: - userGroup | _ - new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _ - } - - @Unroll - def "DeleteUserGroup: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.deleteUserGroup('user-group-id-1') - - then: - 1 * authorizer.getGroup('user-group-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "HasUser: user=#user"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.hasUser('user-id-1') - - then: - 1 * authorizer.getUser('user-id-1') >> user - 0 * _ - result == (user != null) - - where: - user | _ - new User.Builder().identifier('user-id-1').identity('user identity').build() | _ - } - - @Unroll - def "CreateUser: user=#user"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - def result = dao.createUser(requestDTO) - - then: - noExceptionThrown() - - then: - 1 * authorizer.doAddUser(user) >> user - 0 * _ - result?.equals user - - where: - user | users | groups - new User.Builder().identifier('user-id-1') - .identity('user identity').build() | [] as Set | [] as Set - } - - @Unroll - def "GetUser: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.getUser('user-id-1') - - then: - 1 * authorizer.getUser('user-id-1') >> user - result?.equals(user) - 0 * _ - - where: - user | _ - new User.Builder().identifier('user-id-1').identity('user identity').build() | _ - } - - @Unroll - def "GetUser: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.getUser('user-id-1') - - then: - 1 * authorizer.getUser('user-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "GetUsers: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.getUsers() - - then: - 1 * authorizer.getUsers() >> users - result?.containsAll(users) - 0 * _ - - where: - users | _ - [new User.Builder().identifier('user-id-1').identity('user identity').build()] as Set | _ - } - - @Unroll - def "UpdateUser: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - def result = dao.updateUser(requestDTO) - - then: - 1 * authorizer.getUser(requestDTO.id) >> user - 1 * authorizer.doUpdateUser(user) >> user - 0 * _ - result?.equals(user) - - where: - user | users | groups - new User.Builder().identifier('user-id-1') - .identity('user identity').build() | [] as Set | [] as Set - } - - @Unroll - def "UpdateUser: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set) - - when: - dao.updateUser(requestDTO) - - then: - 1 * authorizer.getUser(requestDTO.id) >> null - 0 * _ - thrown ResourceNotFoundException - } - - @Unroll - def "DeleteUser: success"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - def result = dao.deleteUser('user-id-1') - - then: - 1 * authorizer.getUser('user-id-1') >> user - 1 * authorizer.deleteUser(user) >> user - 1 * authorizer.getAccessPolicies() >> [] - 0 * _ - result?.equals(user) - - where: - user | _ - new User.Builder().identifier('user-id-1').identity('user identity').build() | _ - } - - @Unroll - def "DeleteUser: failure"() { - given: - def authorizer = mockAuthorizer() - def dao = new StandardPolicyBasedAuthorizerDAO(authorizer) - - when: - dao.deleteUser('user-id-1') - - then: - 1 * authorizer.getUser('user-id-1') >> null - 0 * _ - thrown ResourceNotFoundException - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardTemplateDAOSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardTemplateDAOSpec.groovy deleted file mode 100644 index 0ebbaf4c5a..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/groovy/org/apache/nifi/web/dao/impl/StandardTemplateDAOSpec.groovy +++ /dev/null @@ -1,160 +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.dao.impl - -import org.apache.nifi.authorization.Authorizer -import org.apache.nifi.controller.FlowController -import org.apache.nifi.controller.flow.FlowManager -import org.apache.nifi.controller.serialization.FlowEncodingVersion -import org.apache.nifi.controller.service.ControllerServiceProvider -import org.apache.nifi.groups.ProcessGroup -import org.apache.nifi.web.api.dto.BundleDTO -import org.apache.nifi.web.api.dto.ComponentDTO -import org.apache.nifi.web.api.dto.DtoFactory -import org.apache.nifi.web.api.dto.FlowSnippetDTO -import org.apache.nifi.web.api.dto.PositionDTO -import org.apache.nifi.web.api.dto.ProcessGroupDTO -import org.apache.nifi.web.api.dto.ProcessorConfigDTO -import org.apache.nifi.web.api.dto.ProcessorDTO -import org.apache.nifi.web.util.SnippetUtils -import spock.lang.Specification -import spock.lang.Unroll - -class StandardTemplateDAOSpec extends Specification { - - @Unroll - def "test InstantiateTemplate moves and scales templates"() { - given: - def flowController = Mock FlowController - def flowManager = Mock FlowManager - flowController.flowManager >> flowManager - def snippetUtils = new SnippetUtils() - snippetUtils.flowController = flowController - def dtoFactory = new DtoFactory() - dtoFactory.authorizer = Mock Authorizer - dtoFactory.controllerServiceProvider = Mock ControllerServiceProvider - snippetUtils.dtoFactory = dtoFactory - def standardTemplateDAO = new StandardTemplateDAO() - standardTemplateDAO.flowController = flowController - standardTemplateDAO.snippetUtils = snippetUtils - def templateEncodingVersion = FlowEncodingVersion.parse(encodingVersion); - // get the major version, or 0 if no version could be parsed - int templateEncodingMajorVersion = templateEncodingVersion != null ? templateEncodingVersion.getMajorVersion() : 0; - double factorX = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_X : 1.0; - double factorY = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_Y : 1.0; - // get all top-level component starting positions - def List<ComponentDTO> components = [snippet.connections + snippet.inputPorts + snippet.outputPorts + snippet.labels + snippet.processGroups + snippet.processGroups + - snippet.processors + snippet.funnels + snippet.remoteProcessGroups].flatten() - - // get all starting subcomponent starting positions - def List<ComponentDTO> subComponents = org.apache.nifi.util.SnippetUtils.findAllProcessGroups(snippet).collect { ProcessGroupDTO processGroup -> - def childSnippet = processGroup.contents - childSnippet.connections + childSnippet.inputPorts + childSnippet.outputPorts + childSnippet.labels + childSnippet.processGroups + childSnippet.processGroups + - childSnippet.processors + childSnippet.funnels + childSnippet.remoteProcessGroups - }.flatten() - - when: - def instantiatedTemplate = standardTemplateDAO.instantiateTemplate(rootGroupId, newOriginX, newOriginY, encodingVersion, snippet, idGenerationSeed) - - then: - flowManager.getGroup(_) >> { String gId -> - def pg = Mock ProcessGroup - pg.identifier >> gId - pg.inputPorts >> [] - pg.outputPorts >> [] - pg.processGroups >> [] - return pg - } - flowManager.rootGroupId >> rootGroupId - flowManager.instantiateSnippet(*_) >> {} - - def instantiatedComponents = [instantiatedTemplate.connections + instantiatedTemplate.inputPorts + instantiatedTemplate.outputPorts + instantiatedTemplate.labels + - instantiatedTemplate.processGroups + instantiatedTemplate.processGroups + instantiatedTemplate.processors + instantiatedTemplate.funnels + - instantiatedTemplate.remoteProcessGroups].flatten() - components.forEach { component -> - def correspondingScaledPosition = instantiatedComponents.find { scaledComponent -> - scaledComponent.name.equals(component.name) - }.position - assert correspondingScaledPosition != null - def expectedPosition = calculateMoveAndScalePosition(component.position, oldOriginX, oldOriginY, newOriginX, newOriginY, factorX, factorY) - assert correspondingScaledPosition.x == expectedPosition.x - assert correspondingScaledPosition.y == expectedPosition.y - - } - def instantiatedSubComponents = org.apache.nifi.util.SnippetUtils.findAllProcessGroups(instantiatedTemplate).collect { ProcessGroupDTO processGroup -> - def childSnippet = processGroup.contents - childSnippet.connections + childSnippet.inputPorts + childSnippet.outputPorts + childSnippet.labels + childSnippet.processGroups + childSnippet.processGroups + - childSnippet.processors + childSnippet.funnels + childSnippet.remoteProcessGroups - }.flatten() - subComponents.forEach { subComponent -> - def correspondingScaledPosition = instantiatedSubComponents.find { scaledComponent -> - scaledComponent.name.equals(subComponent.name) - }.position - assert correspondingScaledPosition != null - def expectedPosition = calculateScalePosition(subComponent.position, factorX, factorY) - assert correspondingScaledPosition.x == expectedPosition.x - assert correspondingScaledPosition.y == expectedPosition.y - - } - - where: - rootGroupId | oldOriginX | oldOriginY | newOriginX | newOriginY | idGenerationSeed | encodingVersion | snippet - 'g1' | 0.0 | 0.0 | 5.0 | 5.0 | 'AAAA' | null | new FlowSnippetDTO() - 'g1' | 10.0 | 10.0 | 5.0 | 5.0 | 'AAAA' | '0.7' | new FlowSnippetDTO( - processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0000-c4af042cb155", name: 'proc1', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))]) - 'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 'AAAA' | null | new FlowSnippetDTO( - processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0001-c4af042cb155", name: 'proc2', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))], - processGroups: [ - new ProcessGroupDTO(id:"c81f6810-0a55-1000-0000-c4af042cb155", - name: 'g2', - position: new PositionDTO(x: 105, y: -10), - contents: new FlowSnippetDTO(processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0002-c4af042cb155", name: 'proc3', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))]) - 'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 'AAAA' | '0.7' | new FlowSnippetDTO( - processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0003-c4af042cb155", name: 'proc2', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))], - processGroups: [ - new ProcessGroupDTO(id:"c81f6810-0a55-1000-0001-c4af042cb155", - name: 'g2', - position: new PositionDTO(x: 105, y: -10), - contents: new FlowSnippetDTO(processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0004-c4af042cb155", name: 'proc3', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))]) - 'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 'AAAA' | '1.0' | new FlowSnippetDTO( - processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0005-c4af042cb155", name: 'proc2', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))], - processGroups: [ - new ProcessGroupDTO(id:"c81f6810-0a55-1000-0003-c4af042cb155", - name: 'g2', - position: new PositionDTO(x: 105, y: -10), - contents: new FlowSnippetDTO(processors: [new ProcessorDTO(id:"c81f6810-0155-1000-0006-c4af042cb155", name: 'proc3', bundle: new BundleDTO("org.apache.nifi", "standard", "1.0"), - config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))]) - } - - def PositionDTO calculateMoveAndScalePosition(position, oldOriginX, oldOriginY, newOriginX, newOriginY, factorX, factorY) { - new PositionDTO( - x: newOriginX + (position.x - oldOriginX) * factorX, - y: newOriginY + (position.y - oldOriginY) * factorY) - } - - def PositionDTO calculateScalePosition(position, factorX, factorY) { - new PositionDTO( - x: position.x * factorX, - y: position.y * factorY) - } -} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestApplicationResource.java new file mode 100644 index 0000000000..ec5b59f4ec --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestApplicationResource.java @@ -0,0 +1,189 @@ +/* + * 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 org.apache.nifi.util.NiFiProperties; +import org.glassfish.jersey.uri.internal.JerseyUriBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.Answer; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.UriBuilderException; +import javax.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.apache.nifi.web.util.WebUtils.FORWARDED_CONTEXT_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.FORWARDED_HOST_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.FORWARDED_PORT_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.FORWARDED_PREFIX_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.FORWARDED_PROTO_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.PROXY_CONTEXT_PATH_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.PROXY_HOST_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.PROXY_PORT_HTTP_HEADER; +import static org.apache.nifi.web.util.WebUtils.PROXY_SCHEME_HTTP_HEADER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TestApplicationResource { + private static final String PROXY_CONTEXT_PATH_PROP = NiFiProperties.WEB_PROXY_CONTEXT_PATH; + private static final String BASE_URI = "https://nifi.apache.org"; + private static final String ALLOWED_PATH = "/some/context/path"; + private static final String FORWARD_SLASH = "/"; + private static final String ACTUAL_RESOURCE = "actualResource"; + private static final String EXPECTED_URI = BASE_URI + ":8081" + ALLOWED_PATH + FORWARD_SLASH + ACTUAL_RESOURCE; + private static final String MULTIPLE_ALLOWED_PATHS = String.join(",", ALLOWED_PATH, "another/path", "a/third/path"); + + @Mock + private HttpServletRequest request; + + private MockApplicationResource resource; + + @BeforeEach + public void setUp(@Mock UriInfo uriInfo) throws Exception { + when(uriInfo.getBaseUriBuilder()).thenReturn(new JerseyUriBuilder().uri(new URI(BASE_URI + FORWARD_SLASH))); + when(request.getScheme()).thenReturn("https"); + + resource = new MockApplicationResource(); + resource.setHttpServletRequest(request); + resource.setUriInfo(uriInfo); + resource.setProperties(new NiFiProperties()); + } + + @Test + public void testGenerateUriShouldBlockProxyContextPathHeaderIfNotInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer()); + assertThrows(UriBuilderException.class, () -> resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowProxyContextPathHeaderIfInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer()); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, ALLOWED_PATH)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowProxyContextPathHeaderIfElementInMultipleAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer()); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, MULTIPLE_ALLOWED_PATHS)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldBlockForwardedContextHeaderIfNotInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_CONTEXT_HTTP_HEADER)); + assertThrows(UriBuilderException.class, () -> resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldBlockForwardedPrefixHeaderIfNotInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_PREFIX_HTTP_HEADER)); + assertThrows(UriBuilderException.class, () -> resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowForwardedContextHeaderIfInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_CONTEXT_HTTP_HEADER)); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, ALLOWED_PATH)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowForwardedPrefixHeaderIfInAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_PREFIX_HTTP_HEADER)); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, ALLOWED_PATH)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowForwardedContextHeaderIfElementInMultipleAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_CONTEXT_HTTP_HEADER)); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, MULTIPLE_ALLOWED_PATHS)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + @Test + public void testGenerateUriShouldAllowForwardedPrefixHeaderIfElementInMultipleAllowList() { + when(request.getHeader(anyString())).thenAnswer(new RequestAnswer(FORWARDED_PREFIX_HTTP_HEADER)); + setNiFiProperties(Collections.singletonMap(PROXY_CONTEXT_PATH_PROP, MULTIPLE_ALLOWED_PATHS)); + + assertEquals(EXPECTED_URI, resource.generateResourceUri(ACTUAL_RESOURCE)); + } + + private void setNiFiProperties(Map<String, String> props) { + resource.properties = new NiFiProperties(props); + } + + private static class MockApplicationResource extends ApplicationResource { + void setHttpServletRequest(HttpServletRequest request) { + super.httpServletRequest = request; + } + + void setUriInfo(UriInfo uriInfo) { + super.uriInfo = uriInfo; + } + } + + private static class RequestAnswer implements Answer<String> { + private final List<String> proxyHeaders; + + public RequestAnswer() { + this(FORWARDED_PREFIX_HTTP_HEADER, FORWARDED_CONTEXT_HTTP_HEADER, PROXY_CONTEXT_PATH_HTTP_HEADER); + } + + public RequestAnswer(String...proxyHeaders) { + this(Arrays.asList(proxyHeaders)); + } + + public RequestAnswer(List<String> proxyHeaders) { + this.proxyHeaders = proxyHeaders; + } + + @Override + public String answer(InvocationOnMock invocationOnMock) { + String argument = invocationOnMock.getArgument(0); + if(proxyHeaders.contains(argument)) { + return ALLOWED_PATH; + } else if(Arrays.asList(FORWARDED_PORT_HTTP_HEADER, PROXY_PORT_HTTP_HEADER).contains(argument)) { + return "8081"; + } else if(Arrays.asList(FORWARDED_PROTO_HTTP_HEADER, PROXY_SCHEME_HTTP_HEADER).contains(argument)) { + return "https"; + } else if(Arrays.asList(PROXY_HOST_HTTP_HEADER, FORWARDED_HOST_HTTP_HEADER).contains(argument)) { + return "nifi.apache.org:8081"; + } else { + return ""; + } + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestProcessGroupResource.java index 39d3299481..f9dd7c5460 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestProcessGroupResource.java @@ -18,47 +18,96 @@ package org.apache.nifi.web.api; import org.apache.nifi.flow.VersionedProcessGroup; import org.apache.nifi.registry.flow.RegisteredFlowSnapshot; +import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.FlowSnippetDTO; +import org.apache.nifi.web.api.dto.TemplateDTO; +import org.apache.nifi.web.api.entity.TemplateEntity; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.Answer; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.ByteArrayInputStream; +import java.util.List; import java.util.UUID; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class TestProcessGroupResource { @InjectMocks - private ProcessGroupResource processGroupResource = new ProcessGroupResource(); + private ProcessGroupResource processGroupResource; @Mock private NiFiServiceFacade serviceFacade; @Test - public void testExportProcessGroup() { + public void testExportProcessGroup(@Mock RegisteredFlowSnapshot versionedFlowSnapshot, @Mock VersionedProcessGroup versionedProcessGroup) { final String groupId = UUID.randomUUID().toString(); - final RegisteredFlowSnapshot versionedFlowSnapshot = mock(RegisteredFlowSnapshot.class); - when(serviceFacade.getCurrentFlowSnapshotByGroupId(groupId)).thenReturn(versionedFlowSnapshot); - - final String flowName = "flowname"; - final VersionedProcessGroup versionedProcessGroup = mock(VersionedProcessGroup.class); when(versionedFlowSnapshot.getFlowContents()).thenReturn(versionedProcessGroup); - when(versionedProcessGroup.getName()).thenReturn(flowName); + when(versionedProcessGroup.getName()).thenReturn("flowname"); - final Response response = processGroupResource.exportProcessGroup(groupId, false); + try(Response response = processGroupResource.exportProcessGroup(groupId, false)) { + assertEquals(200, response.getStatus()); + assertEquals(versionedFlowSnapshot, response.getEntity()); + } + } - final RegisteredFlowSnapshot resultEntity = (RegisteredFlowSnapshot)response.getEntity(); + /** This test creates a malformed template upload request to exercise error handling and sanitization */ + @Test + public void testUploadShouldHandleMalformedTemplate(@Mock HttpServletRequest request, @Mock UriInfo uriInfo) throws Exception { + final String templateWithXssPlain = "<?xml version=\"1.0\" encoding='><script xmlns=\"http://www.w3.org/1999/xhtml\">alert(JSON.stringify(localstorage));</script><errorResponse test='?>"; + Response response = processGroupResource.uploadTemplate(request, uriInfo, "1", + false, new ByteArrayInputStream(templateWithXssPlain.getBytes())); - assertEquals(200, response.getStatus()); - assertEquals(versionedFlowSnapshot, resultEntity); + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertFalse(Pattern.compile("<script.*>").matcher(response.getEntity().toString()).find()); } + /** This test creates a malformed template import request to exercise error handling and sanitization */ + @Test + public void testImportShouldHandleMalformedTemplate(@Mock NiFiProperties niFiProperties, @Mock TemplateResource templateResource, + @Mock TemplateDTO mockIAETemplate, @Mock TemplateDTO mockExceptionTemplate, + @Mock TemplateEntity mockIAETemplateEntity, @Mock TemplateEntity mockExceptionTemplateEntity, + @Mock HttpServletRequest mockRequest) { + when(niFiProperties.isNode()).thenReturn(false); + when(serviceFacade.importTemplate(any(TemplateDTO.class), anyString(), any())).thenAnswer((Answer<TemplateDTO>) invocationOnMock -> invocationOnMock.getArgument(0)); + when(mockIAETemplate.getName()).thenReturn("mockIAETemplate"); + when(mockIAETemplate.getUri()).thenThrow(new IllegalArgumentException("Expected exception with <script> element")); + when(mockIAETemplate.getSnippet()).thenReturn(new FlowSnippetDTO()); + when(mockExceptionTemplate.getName()).thenReturn("mockExceptionTemplate"); + when(mockExceptionTemplate.getUri()).thenThrow(new RuntimeException("Expected exception with <script> element")); + when(mockExceptionTemplate.getSnippet()).thenReturn(new FlowSnippetDTO()); + when(mockIAETemplateEntity.getTemplate()).thenReturn(mockIAETemplate); + when(mockExceptionTemplateEntity.getTemplate()).thenReturn(mockExceptionTemplate); + + processGroupResource.properties = niFiProperties; + processGroupResource.serviceFacade = serviceFacade; + processGroupResource.setTemplateResource(templateResource); + processGroupResource.httpServletRequest = mockRequest; + + List<Response> responses = Stream.of(mockIAETemplateEntity, mockExceptionTemplateEntity) + .map(templateEntity -> processGroupResource.importTemplate(mockRequest, "1", templateEntity)) + .collect(Collectors.toList()); + + responses.forEach(response -> { + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertFalse(Pattern.compile("<script.*>").matcher(response.getEntity().toString()).find()); + }); + } } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.java new file mode 100644 index 0000000000..05a4357dd6 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/config/JsonContentConversionExceptionMapperTest.java @@ -0,0 +1,74 @@ +/* + * 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.config; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.ws.rs.core.Response; +import java.util.regex.Pattern; + +import static com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class JsonContentConversionExceptionMapperTest { + @Mock + private JsonParser mockParser; + + private JsonContentConversionExceptionMapper jsonCCEM; + + @BeforeEach + public void setUp() { + when(mockParser.getTokenLocation()).thenReturn(new JsonLocation(null, 100, 1, 1)); + jsonCCEM = new JsonContentConversionExceptionMapper(); + } + + @Test + public void testShouldThrowExceptionWithStringPortValue() { + try(Response response = jsonCCEM.toResponse(buildInvalidFormatException("thisIsAnInvalidPort"))) { + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + assertEquals("The provided proxyPort value 'thisIsAnInvalidPort' is not" + + " of required type class java.lang.Integer", response.getEntity()); + } + } + + @Test + public void testShouldSanitizeScriptInInput() { + try(Response response = jsonCCEM.toResponse(buildInvalidFormatException("<script>alert(1);</script>"))) { + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + assertFalse(Pattern.compile("<script.*>").matcher(response.getEntity().toString()).find()); + } + } + + private InvalidFormatException buildInvalidFormatException(String value) { + InvalidFormatException ife = InvalidFormatException.from(mockParser, "Some message", value, Integer.class); + wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupDTO", "proxyPort")); + wrapWithPath(ife, new JsonMappingException.Reference("RemoteProcessGroupEntity", "component")); + + return ife; + } +}
