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;
+    }
+}


Reply via email to