datasurvey/src/main/java/org/datasurvey/web/rest/UserResource.java

208 lines
9.5 KiB
Java

package org.datasurvey.web.rest;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.Collections;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import org.datasurvey.config.Constants;
import org.datasurvey.domain.User;
import org.datasurvey.repository.UserRepository;
import org.datasurvey.security.AuthoritiesConstants;
import org.datasurvey.service.MailService;
import org.datasurvey.service.UserService;
import org.datasurvey.service.dto.AdminUserDTO;
import org.datasurvey.web.rest.errors.BadRequestAlertException;
import org.datasurvey.web.rest.errors.EmailAlreadyUsedException;
import org.datasurvey.web.rest.errors.LoginAlreadyUsedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import tech.jhipster.web.util.HeaderUtil;
import tech.jhipster.web.util.PaginationUtil;
import tech.jhipster.web.util.ResponseUtil;
/**
* REST controller for managing users.
* <p>
* This class accesses the {@link User} entity, and needs to fetch its collection of authorities.
* <p>
* For a normal use-case, it would be better to have an eager relationship between User and Authority,
* and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
* which would be good for performance.
* <p>
* We use a View Model and a DTO for 3 reasons:
* <ul>
* <li>We want to keep a lazy association between the user and the authorities, because people will
* quite often do relationships with the user, and we don't want them to get the authorities all
* the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
* application because of this use-case.</li>
* <li> Not having an outer join causes n+1 requests to the database. This is not a real issue as
* we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
* but then all authorities come from the cache, so in fact it's much better than doing an outer join
* (which will get lots of data from the database, for each HTTP call).</li>
* <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
* </ul>
* <p>
* Another option would be to have a specific JPA entity graph to handle this case.
*/
@RestController
@RequestMapping("/api/admin")
public class UserResource {
private static final List<String> ALLOWED_ORDERED_PROPERTIES = Collections.unmodifiableList(
Arrays.asList(
"id",
"login",
"firstName",
"lastName",
"email",
"activated",
"langKey",
"createdBy",
"createdDate",
"lastModifiedBy",
"lastModifiedDate"
)
);
private final Logger log = LoggerFactory.getLogger(UserResource.class);
@Value("${jhipster.clientApp.name}")
private String applicationName;
private final UserService userService;
private final UserRepository userRepository;
private final MailService mailService;
public UserResource(UserService userService, UserRepository userRepository, MailService mailService) {
this.userService = userService;
this.userRepository = userRepository;
this.mailService = mailService;
}
/**
* {@code POST /admin/users} : Creates a new user.
* <p>
* Creates a new user if the login and email are not already used, and sends an
* mail with an activation link.
* The user needs to be activated on creation.
*
* @param userDTO the user to create.
* @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new user, or with status {@code 400 (Bad Request)} if the login or email is already in use.
* @throws URISyntaxException if the Location URI syntax is incorrect.
* @throws BadRequestAlertException {@code 400 (Bad Request)} if the login or email is already in use.
*/
@PostMapping("/users")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<User> createUser(@Valid @RequestBody AdminUserDTO userDTO) throws URISyntaxException {
log.debug("REST request to save User : {}", userDTO);
if (userDTO.getId() != null) {
throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists");
// Lowercase the user login before comparing with database
} else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) {
throw new LoginAlreadyUsedException();
} else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) {
throw new EmailAlreadyUsedException();
} else {
User newUser = userService.createUser(userDTO);
mailService.sendCreationEmail(newUser);
return ResponseEntity
.created(new URI("/api/admin/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert(applicationName, "userManagement.created", newUser.getLogin()))
.body(newUser);
}
}
/**
* {@code PUT /admin/users} : Updates an existing User.
*
* @param userDTO the user to update.
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated user.
* @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already in use.
* @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already in use.
*/
@PutMapping("/users")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<AdminUserDTO> updateUser(@Valid @RequestBody AdminUserDTO userDTO) {
log.debug("REST request to update User : {}", userDTO);
Optional<User> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
throw new EmailAlreadyUsedException();
}
existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
throw new LoginAlreadyUsedException();
}
Optional<AdminUserDTO> updatedUser = userService.updateUser(userDTO);
return ResponseUtil.wrapOrNotFound(
updatedUser,
HeaderUtil.createAlert(applicationName, "userManagement.updated", userDTO.getLogin())
);
}
/**
* {@code GET /admin/users} : get all users with all the details - calling this are only allowed for the administrators.
*
* @param pageable the pagination information.
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users.
*/
@GetMapping("/users")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<List<AdminUserDTO>> getAllUsers(Pageable pageable) {
log.debug("REST request to get all User for an admin");
if (!onlyContainsAllowedProperties(pageable)) {
return ResponseEntity.badRequest().build();
}
final Page<AdminUserDTO> page = userService.getAllManagedUsers(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
private boolean onlyContainsAllowedProperties(Pageable pageable) {
return pageable.getSort().stream().map(Sort.Order::getProperty).allMatch(ALLOWED_ORDERED_PROPERTIES::contains);
}
/**
* {@code GET /admin/users/:login} : get the "login" user.
*
* @param login the login of the user to find.
* @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the "login" user, or with status {@code 404 (Not Found)}.
*/
@GetMapping("/users/{login}")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<AdminUserDTO> getUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) {
log.debug("REST request to get User : {}", login);
return ResponseUtil.wrapOrNotFound(userService.getUserWithAuthoritiesByLogin(login).map(AdminUserDTO::new));
}
/**
* {@code DELETE /admin/users/:login} : delete the "login" User.
*
* @param login the login of the user to delete.
* @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
*/
@DeleteMapping("/users/{login}")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Void> deleteUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) {
log.debug("REST request to delete User: {}", login);
userService.deleteUser(login);
return ResponseEntity.noContent().headers(HeaderUtil.createAlert(applicationName, "userManagement.deleted", login)).build();
}
}