Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In Add users to group, only the first 10 can be viewed or selected #2129

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.openbas.rest.asset_group.form.AssetGroupInput;
import io.openbas.rest.asset_group.form.AssetGroupOutput;
import io.openbas.rest.asset_group.form.UpdateAssetsOnAssetGroupInput;
import io.openbas.rest.helper.RestBehavior;
import io.openbas.telemetry.Tracing;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.validation.Valid;
Expand All @@ -27,7 +28,7 @@
@RestController
@RequiredArgsConstructor
@Secured(ROLE_USER)
public class AssetGroupApi {
public class AssetGroupApi extends RestBehavior {

public static final String ASSET_GROUP_URI = "/api/asset_groups";

Expand Down
45 changes: 30 additions & 15 deletions openbas-api/src/main/java/io/openbas/rest/user/UserApi.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.openbas.rest.user;

import static io.openbas.database.model.User.ROLE_ADMIN;
import static io.openbas.database.specification.UserSpecification.fromIds;
import static io.openbas.helper.DatabaseHelper.updateRelation;
import static io.openbas.helper.StreamHelper.iterableToSet;
import static io.openbas.utils.pagination.PaginationUtils.buildPaginationJPA;

import io.openbas.config.SessionManager;
import io.openbas.database.model.User;
Expand All @@ -19,34 +19,40 @@
import io.openbas.rest.user.form.user.ChangePasswordInput;
import io.openbas.rest.user.form.user.CreateUserInput;
import io.openbas.rest.user.form.user.UpdateUserInput;
import io.openbas.rest.user.form.user.UserOutput;
import io.openbas.rest.user.service.UserCriteriaBuilderService;
import io.openbas.service.MailingService;
import io.openbas.service.UserService;
import io.openbas.telemetry.Tracing;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserApi extends RestBehavior {

public static final String USER_URI = "/api/users";

PassiveExpiringMap<String, String> resetTokenMap = new PassiveExpiringMap<>(1000 * 60 * 10);
@Resource private SessionManager sessionManager;
private OrganizationRepository organizationRepository;
private UserRepository userRepository;
private TagRepository tagRepository;
private UserService userService;
private MailingService mailingService;
private UserCriteriaBuilderService userCriteriaBuilderService;

@Autowired
public void setMailingService(MailingService mailingService) {
Expand All @@ -73,6 +79,11 @@
this.userRepository = userRepository;
}

@Autowired
public void setUserCriteriaBuilderService(UserCriteriaBuilderService userCriteriaBuilderService) {
this.userCriteriaBuilderService = userCriteriaBuilderService;
}

@PostMapping("/api/login")
public User login(@Valid @RequestBody LoginUserInput input) {
Optional<User> optionalUser = userRepository.findByEmailIgnoreCase(input.getLogin());
Expand Down Expand Up @@ -151,18 +162,22 @@
return userRepository.rawAll();
}

@PostMapping("/api/users/search")
public Page<User> users(@RequestBody @Valid final SearchPaginationInput searchPaginationInput) {
return buildPaginationJPA(
(Specification<User> specification, Pageable pageable) ->
this.userRepository.findAll(specification, pageable),
searchPaginationInput,
User.class);
@PostMapping(USER_URI + "/search")
public Page<UserOutput> users(
@RequestBody @Valid final SearchPaginationInput searchPaginationInput) {
return this.userCriteriaBuilderService.userPagination(searchPaginationInput);

Check warning on line 168 in openbas-api/src/main/java/io/openbas/rest/user/UserApi.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/UserApi.java#L168

Added line #L168 was not covered by tests
}

@PostMapping(USER_URI + "/find")
@Transactional(readOnly = true)
@Tracing(name = "Find users", layer = "api", operation = "POST")
public List<UserOutput> findUsers(@RequestBody @Valid @NotNull final List<String> userIds) {
return this.userCriteriaBuilderService.find(fromIds(userIds));

Check warning on line 175 in openbas-api/src/main/java/io/openbas/rest/user/UserApi.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/UserApi.java#L175

Added line #L175 was not covered by tests
}

@Secured(ROLE_ADMIN)
@PutMapping("/api/users/{userId}/password")
@Transactional(rollbackOn = Exception.class)
@Transactional(rollbackFor = Exception.class)
public User changePassword(
@PathVariable String userId, @Valid @RequestBody ChangePasswordInput input) {
User user = userRepository.findById(userId).orElseThrow(ElementNotFoundException::new);
Expand All @@ -172,14 +187,14 @@

@Secured(ROLE_ADMIN)
@PostMapping("/api/users")
@Transactional(rollbackOn = Exception.class)
@Transactional(rollbackFor = Exception.class)
public User createUser(@Valid @RequestBody CreateUserInput input) {
return userService.createUser(input, 1);
}

@Secured(ROLE_ADMIN)
@PutMapping("/api/users/{userId}")
@Transactional(rollbackOn = Exception.class)
@Transactional(rollbackFor = Exception.class)
public User updateUser(@PathVariable String userId, @Valid @RequestBody UpdateUserInput input) {
User user = userRepository.findById(userId).orElseThrow(ElementNotFoundException::new);
user.setUpdateAttributes(input);
Expand All @@ -193,7 +208,7 @@

@Secured(ROLE_ADMIN)
@DeleteMapping("/api/users/{userId}")
@Transactional(rollbackOn = Exception.class)
@Transactional(rollbackFor = Exception.class)
public void deleteUser(@PathVariable String userId) {
sessionManager.invalidateUserSession(userId);
userRepository.deleteById(userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.openbas.rest.user.form.user;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import java.util.Set;
import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class UserOutput {

@JsonProperty("user_id")
@NotBlank
private String id;

@JsonProperty("user_firstname")
private String firstname;

@JsonProperty("user_lastname")
private String lastname;

@JsonProperty("user_email")
@NotBlank
private String email;

@JsonProperty("user_admin")
private boolean admin;

@JsonProperty("user_organization_name")
private String organizationName;

@JsonProperty("user_tags")
private Set<String> tags;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.openbas.rest.user.helper;

import static io.openbas.utils.JpaUtils.createJoinArrayAggOnId;
import static io.openbas.utils.JpaUtils.createLeftJoin;

import io.openbas.database.model.Organization;
import io.openbas.database.model.User;
import io.openbas.rest.user.form.user.UserOutput;
import jakarta.persistence.Tuple;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class UserQueryHelper {

private UserQueryHelper() {}

// -- SELECT --

public static void select(CriteriaBuilder cb, CriteriaQuery<Tuple> cq, Root<User> userRoot) {
// Joins
Join<User, Organization> organizationJoin = createLeftJoin(userRoot, "organization");

Check warning on line 24 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L24

Added line #L24 was not covered by tests
// Array aggregations
Expression<String[]> tagIdsExpression = createJoinArrayAggOnId(cb, userRoot, "tags");

Check warning on line 26 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L26

Added line #L26 was not covered by tests

// Multiselect
cq.multiselect(
userRoot.get("id").alias("user_id"),
userRoot.get("firstname").alias("user_firstname"),
userRoot.get("lastname").alias("user_lastname"),
userRoot.get("email").alias("user_email"),
userRoot.get("admin").alias("user_admin"),
organizationJoin.get("name").alias("user_organization_name"),
tagIdsExpression.alias("user_tags"))
.distinct(true);

Check warning on line 37 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L29-L37

Added lines #L29 - L37 were not covered by tests

// Group by
cq.groupBy(userRoot.get("id"), organizationJoin.get("id"));
}

Check warning on line 41 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L40-L41

Added lines #L40 - L41 were not covered by tests

// -- EXECUTION --

public static List<UserOutput> execution(TypedQuery<Tuple> query) {
return query.getResultList().stream()
.map(

Check warning on line 47 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L46-L47

Added lines #L46 - L47 were not covered by tests
tuple ->
UserOutput.builder()
.id(tuple.get("user_id", String.class))
.firstname(tuple.get("user_firstname", String.class))
.lastname(tuple.get("user_lastname", String.class))
.email(tuple.get("user_email", String.class))
.admin(tuple.get("user_admin", boolean.class))
.organizationName(tuple.get("user_organization_name", String.class))
.tags(
Arrays.stream(tuple.get("user_tags", String[].class))
.collect(Collectors.toSet()))
.build())
.toList();

Check warning on line 60 in openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/helper/UserQueryHelper.java#L49-L60

Added lines #L49 - L60 were not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.openbas.rest.user.service;

import static io.openbas.database.criteria.GenericCriteria.countQuery;
import static io.openbas.rest.user.helper.UserQueryHelper.execution;
import static io.openbas.rest.user.helper.UserQueryHelper.select;
import static io.openbas.utils.pagination.PaginationUtils.buildPaginationCriteriaBuilder;
import static io.openbas.utils.pagination.SortUtilsCriteriaBuilder.toSortCriteriaBuilder;

import io.openbas.database.model.User;
import io.openbas.rest.user.form.user.UserOutput;
import io.openbas.utils.pagination.SearchPaginationInput;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.*;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class UserCriteriaBuilderService {

@PersistenceContext private EntityManager entityManager;

public Page<UserOutput> userPagination(@NotNull SearchPaginationInput searchPaginationInput) {
return buildPaginationCriteriaBuilder(this::paginate, searchPaginationInput, User.class);

Check warning on line 33 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L33

Added line #L33 was not covered by tests
}

public List<UserOutput> find(Specification<User> specification) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();

Check warning on line 37 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L37

Added line #L37 was not covered by tests

CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<User> root = cq.from(User.class);
select(cb, cq, root);

Check warning on line 41 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L39-L41

Added lines #L39 - L41 were not covered by tests

if (specification != null) {
Predicate predicate = specification.toPredicate(root, cq, cb);

Check warning on line 44 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L44

Added line #L44 was not covered by tests
if (predicate != null) {
cq.where(predicate);

Check warning on line 46 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L46

Added line #L46 was not covered by tests
}
}

TypedQuery<Tuple> query = entityManager.createQuery(cq);
return execution(query);

Check warning on line 51 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L50-L51

Added lines #L50 - L51 were not covered by tests
}

// -- PRIVATE --

private Page<UserOutput> paginate(
Specification<User> specification,
Specification<User> specificationCount,
Pageable pageable) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();

Check warning on line 60 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L60

Added line #L60 was not covered by tests

CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<User> userRoot = cq.from(User.class);
select(cb, cq, userRoot);

Check warning on line 64 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L62-L64

Added lines #L62 - L64 were not covered by tests

// -- Specification --
if (specification != null) {
Predicate predicate = specification.toPredicate(userRoot, cq, cb);

Check warning on line 68 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L68

Added line #L68 was not covered by tests
if (predicate != null) {
cq.where(predicate);

Check warning on line 70 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L70

Added line #L70 was not covered by tests
}
}

// -- Sorting --
List<Order> orders = toSortCriteriaBuilder(cb, userRoot, pageable.getSort());
cq.orderBy(orders);

Check warning on line 76 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L75-L76

Added lines #L75 - L76 were not covered by tests

// Type Query
TypedQuery<Tuple> query = entityManager.createQuery(cq);

Check warning on line 79 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L79

Added line #L79 was not covered by tests

// -- Pagination --
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());

Check warning on line 83 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L82-L83

Added lines #L82 - L83 were not covered by tests

// -- EXECUTION --
List<UserOutput> users = execution(query);

Check warning on line 86 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L86

Added line #L86 was not covered by tests

// -- Count Query --
Long total = countQuery(cb, this.entityManager, User.class, specificationCount);

Check warning on line 89 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L89

Added line #L89 was not covered by tests

return new PageImpl<>(users, pageable, total);

Check warning on line 91 in openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java

View check run for this annotation

Codecov / codecov/patch

openbas-api/src/main/java/io/openbas/rest/user/service/UserCriteriaBuilderService.java#L91

Added line #L91 was not covered by tests
}
}
6 changes: 6 additions & 0 deletions openbas-front/src/actions/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export const searchUsers = (paginationInput) => {
return simplePostCall(uri, data);
};

export const findUsers = (userIds) => {
const data = userIds;
const uri = '/api/users/find';
return simplePostCall(uri, data);
};

export const addUser = data => dispatch => postReferential(schema.user, '/api/users', data)(dispatch);

export const updateUserPassword = (userId, data) => dispatch => putReferential(schema.user, `/api/users/${userId}/password`, data)(dispatch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ const AssetGroupDialogAdding: FunctionComponent<Props> = ({
// Pagination
const [assetGroups, setAssetGroups] = useState<AssetGroupOutput[]>([]);

const availableFilterNames = [
'asset_group_tags',
];
const { queryableHelpers, searchPaginationInput } = useQueryable(buildSearchPagination({}));

const paginationComponent = (
Expand All @@ -76,7 +73,7 @@ const AssetGroupDialogAdding: FunctionComponent<Props> = ({
searchPaginationInput={searchPaginationInput}
setContent={setAssetGroups}
entityPrefix="asset_group"
availableFilterNames={availableFilterNames}
availableFilterNames={['asset_group_tags']}
queryableHelpers={queryableHelpers}
/>
);
Expand Down
Loading
Loading