mirror of https://github.com/apache/nifi.git
NIFI-11461 Improve User and Group Tenants Search (#7181)
* NIFI-11461 Improved User and Group Tenants Search - Added searchTenants method to NiFiServiceFacade and removed unnecessary object creation - Updated TenantsResource to use delegated NiFiServiceFacade.searchTenants method - Changed autocomplete delay from default 300 ms to 500 ms * NIFI-11461 Adjusted implementation to use EntityFactory.createTenantEntity This closes #7181
This commit is contained in:
parent
382058e154
commit
bdff3abcd6
|
@ -128,6 +128,7 @@ import org.apache.nifi.web.api.entity.SnippetEntity;
|
|||
import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity;
|
||||
import org.apache.nifi.web.api.entity.StatusHistoryEntity;
|
||||
import org.apache.nifi.web.api.entity.TemplateEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
import org.apache.nifi.web.api.entity.VariableRegistryEntity;
|
||||
|
@ -1960,6 +1961,14 @@ public interface NiFiServiceFacade {
|
|||
*/
|
||||
Set<UserEntity> getUsers();
|
||||
|
||||
/**
|
||||
* Search for User and Group Tenants based on provided query string
|
||||
*
|
||||
* @param query Search query where null or empty returns unfiltered results
|
||||
* @return Tenants Entity with zero or more matched Users and User Groups
|
||||
*/
|
||||
TenantsEntity searchTenants(String query);
|
||||
|
||||
/**
|
||||
* Updates the specified user.
|
||||
* @param revision Revision to compare with current base revision
|
||||
|
|
|
@ -237,6 +237,7 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
|
|||
import org.apache.nifi.web.api.dto.SnippetDTO;
|
||||
import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO;
|
||||
import org.apache.nifi.web.api.dto.TemplateDTO;
|
||||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
import org.apache.nifi.web.api.dto.UserDTO;
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO;
|
||||
import org.apache.nifi.web.api.dto.VariableRegistryDTO;
|
||||
|
@ -317,6 +318,7 @@ import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity;
|
|||
import org.apache.nifi.web.api.entity.StatusHistoryEntity;
|
||||
import org.apache.nifi.web.api.entity.TemplateEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
import org.apache.nifi.web.api.entity.VariableEntity;
|
||||
|
@ -384,6 +386,8 @@ import java.util.function.Supplier;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
|
||||
|
||||
/**
|
||||
* Implementation of NiFiServiceFacade that performs revision checking.
|
||||
*/
|
||||
|
@ -4461,6 +4465,46 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
|
|||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for User and Group Tenants with optimized conversion from specific objects to Tenant objects
|
||||
*
|
||||
* @param query Search query where null or empty returns unfiltered results
|
||||
* @return Tenants Entity containing zero or more matching Users and Groups
|
||||
*/
|
||||
@Override
|
||||
public TenantsEntity searchTenants(final String query) {
|
||||
final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getTenant());
|
||||
|
||||
final Set<TenantEntity> usersFound = userDAO.getUsers()
|
||||
.stream()
|
||||
.filter(user -> isMatched(user.getIdentity(), query))
|
||||
.map(user -> {
|
||||
final TenantDTO tenant = dtoFactory.createTenantDTO(user);
|
||||
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(tenant.getId()));
|
||||
return entityFactory.createTenantEntity(tenant, revision, permissions);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
final Set<TenantEntity> userGroupsFound = userGroupDAO.getUserGroups()
|
||||
.stream()
|
||||
.filter(userGroup -> isMatched(userGroup.getName(), query))
|
||||
.map(userGroup -> {
|
||||
final TenantDTO tenant = dtoFactory.createTenantDTO(userGroup);
|
||||
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(tenant.getId()));
|
||||
return entityFactory.createTenantEntity(tenant, revision, permissions);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
final TenantsEntity tenantsEntity = new TenantsEntity();
|
||||
tenantsEntity.setUsers(usersFound);
|
||||
tenantsEntity.setUserGroups(userGroupsFound);
|
||||
return tenantsEntity;
|
||||
}
|
||||
|
||||
private boolean isMatched(final String label, final String query) {
|
||||
return StringUtils.isEmpty(query) || containsIgnoreCase(label, query);
|
||||
}
|
||||
|
||||
private UserEntity createUserEntity(final User user, final boolean enforceUserExistence) {
|
||||
final RevisionDTO userRevision = dtoFactory.createRevisionDTO(revisionManager.getRevision(user.getIdentifier()));
|
||||
final PermissionsDTO permissions = dtoFactory.createPermissionsDto(authorizableLookup.getTenant());
|
||||
|
|
|
@ -35,10 +35,8 @@ import org.apache.nifi.util.NiFiProperties;
|
|||
import org.apache.nifi.web.NiFiServiceFacade;
|
||||
import org.apache.nifi.web.Revision;
|
||||
import org.apache.nifi.web.api.dto.RevisionDTO;
|
||||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
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 org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
|
@ -64,9 +62,7 @@ import javax.ws.rs.core.Context;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Path("tenants")
|
||||
|
@ -941,53 +937,7 @@ public class TenantsResource extends ApplicationResource {
|
|||
tenants.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
|
||||
});
|
||||
|
||||
final List<TenantEntity> userMatches = new ArrayList<>();
|
||||
final List<TenantEntity> userGroupMatches = new ArrayList<>();
|
||||
|
||||
// get the users
|
||||
for (final UserEntity userEntity : serviceFacade.getUsers()) {
|
||||
final UserDTO user = userEntity.getComponent();
|
||||
if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(user.getIdentity(), value)) {
|
||||
final TenantDTO tenant = new TenantDTO();
|
||||
tenant.setId(user.getId());
|
||||
tenant.setIdentity(user.getIdentity());
|
||||
tenant.setConfigurable(user.getConfigurable());
|
||||
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setPermissions(userEntity.getPermissions());
|
||||
entity.setRevision(userEntity.getRevision());
|
||||
entity.setId(userEntity.getId());
|
||||
entity.setComponent(tenant);
|
||||
|
||||
userMatches.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// get the user groups
|
||||
for (final UserGroupEntity userGroupEntity : serviceFacade.getUserGroups()) {
|
||||
final UserGroupDTO userGroup = userGroupEntity.getComponent();
|
||||
if (StringUtils.isBlank(value) || StringUtils.containsIgnoreCase(userGroup.getIdentity(), value)) {
|
||||
final TenantDTO tenant = new TenantDTO();
|
||||
tenant.setId(userGroup.getId());
|
||||
tenant.setIdentity(userGroup.getIdentity());
|
||||
tenant.setConfigurable(userGroup.getConfigurable());
|
||||
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setPermissions(userGroupEntity.getPermissions());
|
||||
entity.setRevision(userGroupEntity.getRevision());
|
||||
entity.setId(userGroupEntity.getId());
|
||||
entity.setComponent(tenant);
|
||||
|
||||
userGroupMatches.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// build the response
|
||||
final TenantsEntity results = new TenantsEntity();
|
||||
results.setUsers(userMatches);
|
||||
results.setUserGroups(userGroupMatches);
|
||||
|
||||
// generate an 200 - OK response
|
||||
final TenantsEntity results = serviceFacade.searchTenants(value);
|
||||
return noCache(Response.ok(results)).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ import org.apache.nifi.authorization.AuthorizationRequest;
|
|||
import org.apache.nifi.authorization.AuthorizationResult;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.ComponentAuthorizable;
|
||||
import org.apache.nifi.authorization.Group;
|
||||
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.resource.ResourceType;
|
||||
|
@ -66,9 +68,13 @@ import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
|
|||
import org.apache.nifi.web.api.entity.ActionEntity;
|
||||
import org.apache.nifi.web.api.entity.ProcessGroupEntity;
|
||||
import org.apache.nifi.web.api.entity.StatusHistoryEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
import org.apache.nifi.web.controller.ControllerFacade;
|
||||
import org.apache.nifi.web.dao.ProcessGroupDAO;
|
||||
import org.apache.nifi.web.dao.RemoteProcessGroupDAO;
|
||||
import org.apache.nifi.web.dao.UserDAO;
|
||||
import org.apache.nifi.web.dao.UserGroupDAO;
|
||||
import org.apache.nifi.web.revision.RevisionManager;
|
||||
import org.apache.nifi.web.revision.RevisionUpdate;
|
||||
import org.apache.nifi.web.revision.StandardRevisionUpdate;
|
||||
|
@ -81,8 +87,10 @@ import org.mockito.Mockito;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -105,6 +113,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
|
|||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anySet;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.same;
|
||||
|
@ -113,11 +122,14 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
public class StandardNiFiServiceFacadeTest {
|
||||
|
||||
private static final String USER_1 = "user-1";
|
||||
private static final String USER_2 = "user-2";
|
||||
private static final String USER_PREFIX = "user";
|
||||
private static final String USER_1 = String.format("%s-1", USER_PREFIX);
|
||||
private static final String USER_1_ID = UUID.nameUUIDFromBytes(USER_1.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
private static final String USER_2 = String.format("%s-2", USER_PREFIX);
|
||||
private static final String USER_GROUP_1 = String.format("%s-group-1", USER_PREFIX);
|
||||
private static final String USER_GROUP_1_ID = UUID.nameUUIDFromBytes(USER_GROUP_1.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
|
||||
private static final Integer UNKNOWN_ACTION_ID = 0;
|
||||
|
||||
|
@ -727,6 +739,99 @@ public class StandardNiFiServiceFacadeTest {
|
|||
assertEquals(groupId, result.getBulletins().get(0).getGroupId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTenantsNullQuery() {
|
||||
setupSearchTenants();
|
||||
|
||||
final TenantsEntity tenantsEntity = serviceFacade.searchTenants(null);
|
||||
|
||||
assertUserFound(tenantsEntity);
|
||||
assertUserGroupFound(tenantsEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTenantsMatchedQuery() {
|
||||
setupSearchTenants();
|
||||
|
||||
final TenantsEntity tenantsEntity = serviceFacade.searchTenants(USER_PREFIX);
|
||||
|
||||
assertUserFound(tenantsEntity);
|
||||
assertUserGroupFound(tenantsEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTenantsGroupMatchedQuery() {
|
||||
setupSearchTenants();
|
||||
|
||||
final TenantsEntity tenantsEntity = serviceFacade.searchTenants(USER_GROUP_1);
|
||||
|
||||
assertUserGroupFound(tenantsEntity);
|
||||
|
||||
final Collection<TenantEntity> usersFound = tenantsEntity.getUsers();
|
||||
assertTrue(usersFound.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTenantsNotMatchedQuery() {
|
||||
setupSearchTenants();
|
||||
|
||||
final TenantsEntity tenantsEntity = serviceFacade.searchTenants(String.class.getSimpleName());
|
||||
|
||||
assertNotNull(tenantsEntity);
|
||||
|
||||
final Collection<TenantEntity> usersFound = tenantsEntity.getUsers();
|
||||
assertTrue(usersFound.isEmpty());
|
||||
|
||||
final Collection<TenantEntity> userGroupsFound = tenantsEntity.getUserGroups();
|
||||
assertTrue(userGroupsFound.isEmpty());
|
||||
}
|
||||
|
||||
private void setupSearchTenants() {
|
||||
final Authentication authentication = new NiFiAuthenticationToken(new NiFiUserDetails(new Builder().identity(USER_1).build()));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
authorizer = mock(Authorizer.class);
|
||||
serviceFacade.setAuthorizer(authorizer);
|
||||
final AuthorizableLookup authorizableLookup = mock(AuthorizableLookup.class);
|
||||
serviceFacade.setAuthorizableLookup(authorizableLookup);
|
||||
|
||||
final Authorizable authorizable = mock(Authorizable.class);
|
||||
when(authorizableLookup.getTenant()).thenReturn(authorizable);
|
||||
|
||||
final RevisionManager revisionManager = mock(RevisionManager.class);
|
||||
serviceFacade.setRevisionManager(revisionManager);
|
||||
final Revision revision = new Revision(1L, USER_1_ID, USER_1_ID);
|
||||
when(revisionManager.getRevision(anyString())).thenReturn(revision);
|
||||
|
||||
final UserDAO userDAO = mock(UserDAO.class);
|
||||
serviceFacade.setUserDAO(userDAO);
|
||||
final UserGroupDAO userGroupDAO = mock(UserGroupDAO.class);
|
||||
serviceFacade.setUserGroupDAO(userGroupDAO);
|
||||
|
||||
final User user = new User.Builder().identity(USER_1).identifier(USER_1_ID).build();
|
||||
final Set<User> users = Collections.singleton(user);
|
||||
when(userDAO.getUsers()).thenReturn(users);
|
||||
|
||||
final Group group = new Group.Builder().name(USER_GROUP_1).identifier(USER_GROUP_1_ID).build();
|
||||
final Set<Group> groups = Collections.singleton(group);
|
||||
when(userGroupDAO.getUserGroups()).thenReturn(groups);
|
||||
}
|
||||
|
||||
private void assertUserFound(final TenantsEntity tenantsEntity) {
|
||||
assertNotNull(tenantsEntity);
|
||||
final Collection<TenantEntity> usersFound = tenantsEntity.getUsers();
|
||||
assertFalse(usersFound.isEmpty());
|
||||
final TenantEntity firstUserFound = usersFound.iterator().next();
|
||||
assertEquals(USER_1_ID, firstUserFound.getId());
|
||||
}
|
||||
|
||||
private void assertUserGroupFound(final TenantsEntity tenantsEntity) {
|
||||
assertNotNull(tenantsEntity);
|
||||
final Collection<TenantEntity> userGroupsFound = tenantsEntity.getUserGroups();
|
||||
assertFalse(userGroupsFound.isEmpty());
|
||||
final TenantEntity firstUserGroup = userGroupsFound.iterator().next();
|
||||
assertEquals(USER_GROUP_1_ID, firstUserGroup.getId());
|
||||
}
|
||||
|
||||
private static class MockTestBulletinRepository extends MockBulletinRepository {
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
|
||||
// configure the user auto complete
|
||||
$.widget('nf.userSearchAutocomplete', $.ui.autocomplete, {
|
||||
delay: 500,
|
||||
reset: function () {
|
||||
this.term = null;
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue