Skip to content
Snippets Groups Projects
Commit 64e5e03f authored by kbeyro's avatar kbeyro
Browse files

add user pagging

parent ab4f3fe8
No related branches found
No related tags found
2 merge requests!273Release 1.8.0 update,!247Resolve "Revisit paging mechanism for endpoints providing data for table/list views"
package net.geant.nmaas.portal.api.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.geant.nmaas.portal.persistent.entity.Role;
import net.geant.nmaas.portal.persistent.entity.User;
import net.geant.nmaas.portal.persistent.entity.UserRole;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserListEntry extends UserBase implements Serializable {
protected String name;
protected String email;
protected OffsetDateTime lastSuccessfulLoginDate;
protected OffsetDateTime firstLoginDate;
protected Set<UserRoleView> roles = new HashSet<>();
protected String globalRole;
protected Set<String> domainsName;
public UserListEntry(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.name = user.getFirstname() != null ? user.getFirstname() : "";
if (user.getLastname() != null && !user.getLastname().isEmpty()) {
this.name += (this.name.isEmpty() ? "" : " ") + user.getLastname();
}
this.email = user.getEmail();
this.domainsName = user.getRoles().stream()
.filter(userRole -> userRole.getDomain() != null && !Objects.equals(userRole.getDomain().getName(), "GLOBAL"))
.map(userRole -> userRole.getDomain().getName())
.collect(Collectors.toSet());
List<Role> globalRoleList = List.of(Role.ROLE_GUEST, Role.ROLE_SYSTEM_ADMIN, Role.ROLE_TOOL_MANAGER, Role.ROLE_OPERATOR, Role.ROLE_GROUP_MANAGER);
Optional<Role> globalRole = user.getRoles().stream()
.map(UserRole::getRole)
.filter(globalRoleList::contains).findFirst();
this.globalRole = globalRole.map(Enum::name).orElse("");
this.enabled = user.isEnabled();
this.roles = user.getRoles().stream().map(r -> new UserRoleView(r.getRole(), r.getDomain().getId(), r.getDomain().getName())).collect(Collectors.toSet());
}
}
......@@ -11,6 +11,7 @@ import net.geant.nmaas.orchestration.jobs.UserDomainAssignmentJob;
import net.geant.nmaas.portal.api.domain.PasswordChange;
import net.geant.nmaas.portal.api.domain.PasswordReset;
import net.geant.nmaas.portal.api.domain.UserBase;
import net.geant.nmaas.portal.api.domain.UserListEntry;
import net.geant.nmaas.portal.api.domain.UserRequest;
import net.geant.nmaas.portal.api.domain.UserRoleView;
import net.geant.nmaas.portal.api.domain.UserView;
......@@ -37,7 +38,10 @@ import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
......@@ -69,13 +73,13 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_DOMAIN_ADMIN;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_GROUP_DOMAIN_ADMIN;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_GROUP_MANAGER;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_GUEST;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_OPERATOR;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_SYSTEM_ADMIN;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_TOOL_MANAGER;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_USER;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_GROUP_DOMAIN_ADMIN;
import static net.geant.nmaas.portal.persistent.entity.Role.ROLE_GROUP_MANAGER;
@RestController
@RequestMapping("/api")
......@@ -166,6 +170,55 @@ public class UsersController {
.collect(Collectors.toList());
}
@GetMapping("/users/list")
@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')")
@Transactional
public Page<UserListEntry> getUsersList(@PageableDefault(page = 0, size = 15, sort = "id") Pageable pageable,
@RequestParam(required = false) String searchValue,
Principal principal) {
User owner = this.userService.findByUsername(principal.getName()).orElseThrow(
() -> new RuntimeException("User with username: " + principal.getName() + " does not exist"));
Map<Long, UserLoginDate> userLoginDateMap = this.userLoginService.getAllFirstAndLastSuccessfulLoginDate().stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getUserId(), x))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (owner.getRoles().stream().anyMatch(role -> role.getRole() == ROLE_SYSTEM_ADMIN)) {
return userService.findAllListEntry(pageable, searchValue).map(u -> mapUser(u, userLoginDateMap));
} else {
throw new RuntimeException("User with username: " + principal.getName() + " doesnt have access to this list");
}
}
@GetMapping("/domains/{domainId}/users/list")
@PreAuthorize("hasPermission(#domainId, 'domain', 'OWNER')")
public Page<UserListEntry> getUsersListDomain(@PageableDefault(page = 0, size = 15, sort = "id") Pageable pageable,
@RequestParam(required = false) String searchValue,
@PathVariable Long domainId) {
Map<Long, UserLoginDate> userLoginDateMap = this.userLoginService.getAllFirstAndLastSuccessfulLoginDate().stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getUserId(), x))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
List<UserListEntry> allUserInDomain = domainService.getMembers(domainId).stream().map(UserListEntry::new).map(u -> mapUser(u, userLoginDateMap)).toList();
int pageSize = pageable.getPageSize();
int currentPage = pageable.getPageNumber();
int startItem = currentPage * pageSize;
int endItem = Math.min(startItem + pageSize, allUserInDomain.size());
List<UserListEntry> pageContent;
if (allUserInDomain.size() < startItem) {
pageContent = new ArrayList<>();
} else {
pageContent = allUserInDomain.subList(startItem, endItem);
}
return new PageImpl<>(pageContent, pageable, allUserInDomain.size());
}
@GetMapping(value = "/users/{userId}")
@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') or hasRole('ROLE_DOMAIN_ADMIN')")
public UserBase retrieveUser(@PathVariable("userId") Long userId, Principal principal) {
......@@ -519,8 +572,8 @@ public class UsersController {
try {
domainService.addMemberRole(domain.getId(), user.getId(), role);
if (!domain.equals(globalDomain)){
webhookEventRepository.findIdByEventType(WebhookEventType.USER_ASSIGNMENT).forEach(id -> scheduleManager.createOneTimeJob(UserDomainAssignmentJob.class, "UserDomainAssignmentJobCreate_" + id + "_user" + user.getId()+ "_domain" + domain.getId()+"_" + LocalDateTime.now(), Map.of("webhookId", id, "domainId", domain.getId(), "userId", user.getId(), "role", role.name(), "action", "create")));
if (!domain.equals(globalDomain)) {
webhookEventRepository.findIdByEventType(WebhookEventType.USER_ASSIGNMENT).forEach(id -> scheduleManager.createOneTimeJob(UserDomainAssignmentJob.class, "UserDomainAssignmentJobCreate_" + id + "_user" + user.getId() + "_domain" + domain.getId() + "_" + LocalDateTime.now(), Map.of("webhookId", id, "domainId", domain.getId(), "userId", user.getId(), "role", role.name(), "action", "create")));
}
final User adminUser = userService.findByUsername(principal.getName()).orElseThrow(() -> new ObjectNotFoundException(USER_NOT_FOUND_ERROR_MESSAGE));
......@@ -556,8 +609,8 @@ public class UsersController {
final User adminUser = userService.findByUsername(principal.getName()).orElseThrow(() -> new ObjectNotFoundException(USER_NOT_FOUND_ERROR_MESSAGE));
final String adminRoles = getRoleAsString(adminUser.getRoles());
final Domain globalDomain = domainService.getGlobalDomain().orElse(null);
if (!domain.equals(globalDomain)){
webhookEventRepository.findIdByEventType(WebhookEventType.USER_ASSIGNMENT).forEach(id -> scheduleManager.createOneTimeJob(UserDomainAssignmentJob.class, "UserDomainAssignmentJobDelete_" + id + "_user" + user.getId()+ "_domain" + domain.getId()+"_" + LocalDateTime.now(), Map.of("webhookId", id, "domainId", domain.getId(), "userId", user.getId(), "role", role.name(), "action", "delete")));
if (!domain.equals(globalDomain)) {
webhookEventRepository.findIdByEventType(WebhookEventType.USER_ASSIGNMENT).forEach(id -> scheduleManager.createOneTimeJob(UserDomainAssignmentJob.class, "UserDomainAssignmentJobDelete_" + id + "_user" + user.getId() + "_domain" + domain.getId() + "_" + LocalDateTime.now(), Map.of("webhookId", id, "domainId", domain.getId(), "userId", user.getId(), "role", role.name(), "action", "delete")));
}
log.info(String.format("User [%s] with role [%s] removed role [%s] of user name [%s] in domain [%d].",
......@@ -645,9 +698,9 @@ public class UsersController {
.collect(Collectors.toList());
return allUsers.stream()
.filter(user -> user.getEmail().toLowerCase().contentEquals(search))
.map(this::mapMinimalUser).collect(Collectors.toList());
}
.filter(user -> user.getEmail().toLowerCase().contentEquals(search))
.map(this::mapMinimalUser).collect(Collectors.toList());
}
private Role convertRole(String userRole) {
Role role;
......@@ -744,4 +797,12 @@ public class UsersController {
return userViewMinimal;
}
private UserListEntry mapUser(UserListEntry entry, final Map<Long, UserLoginDate> userLoginDateMap) {
if (userLoginDateMap.containsKey(entry.getId())) {
entry.setLastSuccessfulLoginDate(userLoginDateMap.get(entry.getId()).getMaxLoginDate());
entry.setFirstLoginDate(userLoginDateMap.get(entry.getId()).getMinLoginDate());
}
return entry;
}
}
package net.geant.nmaas.portal.persistent.repositories;
import net.geant.nmaas.portal.api.domain.UserListEntry;
import net.geant.nmaas.portal.persistent.entity.Domain;
import net.geant.nmaas.portal.persistent.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
boolean existsByUsername(String username);
......@@ -42,10 +49,12 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT count(distinct u.id) FROM User u JOIN UserRole r ON r.id.user.id = u.id WHERE r.id.domain.id != 1")
int countWithDomain();
// @Query("Select new net.geant.nmaas.portal.api.domain.UserListEntry(u.id, u.username, u.firstname, u.lastname, u.email, u.enabled) FROM User u")
// List<UserListEntry> findAllListEntry();
//
// @Query("Select new net.geant.nmaas.portal.api.domain.UserListEntry(u.id, u.username, u.firstname, u.lastname, u.email, u.enabled) FROM User u")
// Page<UserListEntry> findAllListEntry(Pageable pageable);
@Query("Select new net.geant.nmaas.portal.api.domain.UserListEntry(u) FROM User u")
List<UserListEntry> findAllListEntry();
@Query("Select new net.geant.nmaas.portal.api.domain.UserListEntry(u) FROM User u")
Page<UserListEntry> findAllListEntry(Pageable pageable);
Page<User> findAll(Specification<User> spec, Pageable pageable);
}
......@@ -14,7 +14,7 @@ public class DomainSpecification {
public static Specification<Domain> containsTextInAttributes(String searchText, String... attributes) {
if (searchText == null || searchText.trim().isEmpty()) {
return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); // Zwraca zawsze prawdziwy predykat (AND)
return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction();
}
final String lowerCaseSearchText = "%" + searchText.toLowerCase() + "%";
......
package net.geant.nmaas.portal.persistent.spec;
import jakarta.persistence.criteria.Predicate;
import net.geant.nmaas.portal.persistent.entity.User;
import org.springframework.data.jpa.domain.Specification;
import java.util.ArrayList;
import java.util.List;
public class UserSpecification {
public static Specification<User> findBySearchValue(String searchValue) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
query.distinct(true);
if (searchValue != null && !searchValue.trim().isEmpty()) {
String lowerCaseSearchValue = searchValue.toLowerCase().trim();
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("username")),
"%" + lowerCaseSearchValue + "%"));
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("id").as(String.class)),
"%" + lowerCaseSearchValue + "%"));
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("firstname")),
"%" + lowerCaseSearchValue + "%"));
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("lastname")),
"%" + lowerCaseSearchValue + "%"));
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("email")),
"%" + lowerCaseSearchValue + "%"));
if ("true".equals(lowerCaseSearchValue) || "false".equals(lowerCaseSearchValue)) {
predicates.add(criteriaBuilder.equal(root.get("enabled"), Boolean.valueOf(lowerCaseSearchValue)));
} else {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("enabled").as(String.class)),
"%" + lowerCaseSearchValue + "%"));
}
return criteriaBuilder.or(predicates.toArray(new Predicate[0]));
}
return criteriaBuilder.isTrue(criteriaBuilder.literal(true));
};
}
}
......@@ -2,6 +2,7 @@ package net.geant.nmaas.portal.service;
import net.geant.nmaas.portal.api.auth.Registration;
import net.geant.nmaas.portal.api.bulk.CsvDomain;
import net.geant.nmaas.portal.api.domain.UserListEntry;
import net.geant.nmaas.portal.api.domain.UserView;
import net.geant.nmaas.portal.persistent.entity.Domain;
import net.geant.nmaas.portal.persistent.entity.Role;
......@@ -71,4 +72,12 @@ public interface UserService {
boolean isAdmin(String username);
Page<UserListEntry> findAllListEntry(Pageable pageable, String searchValue);
Page<UserListEntry> findAllListEntry(Pageable pageable, String searchValue, Long domainId);
List<UserListEntry> findAllListEntry();
}
......@@ -10,6 +10,7 @@ import net.geant.nmaas.notifications.NotificationEvent;
import net.geant.nmaas.notifications.templates.MailType;
import net.geant.nmaas.portal.api.auth.Registration;
import net.geant.nmaas.portal.api.bulk.CsvDomain;
import net.geant.nmaas.portal.api.domain.UserListEntry;
import net.geant.nmaas.portal.api.domain.UserView;
import net.geant.nmaas.portal.api.exceptions.MissingElementException;
import net.geant.nmaas.portal.api.exceptions.ProcessingException;
......@@ -21,8 +22,11 @@ import net.geant.nmaas.portal.persistent.entity.User;
import net.geant.nmaas.portal.persistent.entity.UserRole;
import net.geant.nmaas.portal.persistent.repositories.UserRepository;
import net.geant.nmaas.portal.persistent.repositories.UserRoleRepository;
import net.geant.nmaas.portal.persistent.results.UserLoginDate;
import net.geant.nmaas.portal.persistent.spec.UserSpecification;
import net.geant.nmaas.portal.service.ConfigurationManager;
import net.geant.nmaas.portal.service.DomainGroupService;
import net.geant.nmaas.portal.service.UserLoginRegisterService;
import net.geant.nmaas.portal.service.UserService;
import org.apache.commons.lang3.RandomStringUtils;
import org.modelmapper.ModelMapper;
......@@ -30,12 +34,15 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
......@@ -58,6 +65,9 @@ public class UserServiceImpl implements UserService {
private final JWTTokenService jwtTokenService;
private final DomainGroupService domainGroupService;
private final UserLoginRegisterService userLoginService;
@Value("${portal.address}")
@Setter
private String portalAddress;
......@@ -327,6 +337,31 @@ public class UserServiceImpl implements UserService {
return result;
}
@Override
public Page<UserListEntry> findAllListEntry(Pageable pageable, String searchValue) {
Map<Long, UserLoginDate> userLoginDateMap = this.userLoginService.getAllFirstAndLastSuccessfulLoginDate().stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getUserId(), x))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (searchValue != null && !searchValue.isEmpty()) {
Specification<User> searchSpec = UserSpecification.findBySearchValue(searchValue);
Page<User> all = userRepository.findAll(searchSpec, pageable);
return all.map(this::toListView).map(u -> mapUser(u, userLoginDateMap));
} else {
return userRepository.findAllListEntry(pageable);
}
}
@Override
public Page<UserListEntry> findAllListEntry(Pageable pageable, String searchValue, Long domainId) {
return null;
}
@Override
public List<UserListEntry> findAllListEntry() {
return userRepository.findAllListEntry();
}
private void sendMail(User user, MailType mailType) {
ImmutableMap<String, Object> map;
if (mailType == MailType.NEW_BULK_LOGIN) {
......@@ -359,4 +394,16 @@ public class UserServiceImpl implements UserService {
}
return url + "reset/" + token;
}
private UserListEntry mapUser(UserListEntry entry,final Map<Long, UserLoginDate> userLoginDateMap ) {
if (userLoginDateMap.containsKey(entry.getId())) {
entry.setLastSuccessfulLoginDate(userLoginDateMap.get(entry.getId()).getMaxLoginDate());
entry.setFirstLoginDate(userLoginDateMap.get(entry.getId()).getMinLoginDate());
}
return entry;
}
private UserListEntry toListView(User user) {
return new UserListEntry(user);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment