UserRoleController.java
package api.controllers.users;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import api.dtos.ErrorDto;
import api.dtos.RoleDto;
import api.entities.Role;
import api.entities.User;
import api.mapper.RoleMapper;
import api.services.RoleService;
import api.services.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* {@link UserRoleController}.
*/
@RestController
@RequestMapping("/users/{userId}/roles")
@Tag(name = "User Roles", description = "The roles applied to a specific user.")
public class UserRoleController {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private RoleMapper roleMapper;
/**
* Get user's roles.
*
* @param userId User id
* @return {@link List} of {@link RoleDto}
*/
// region
@Operation(
summary = "Get User's Roles",
description = "Get list of roles applied to a user."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = RoleDto.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@GetMapping("")
@PreAuthorize("hasAuthority(@DbSetup.USER_READ) and hasAuthority(@DbSetup.ROLE_READ)")
public List<RoleDto> getUserRoles(@PathVariable int userId) {
Set<Role> roles = userService.getRoles(userId);
return roles.stream()
.map(roleMapper::toDto)
.collect(Collectors.toList());
}
/**
* Set user's roles.
*
* @param userId User id
* @param roleIds Role ids
* @return {@link List} of {@link RoleDto}
* @apiNote Does not throw error if roles not found
*/
// region
@Operation(
summary = "Set User's Roles",
description = "Set list of roles applied to a user."
+ "<ul>"
+ "<li>If a role ID is not found in the system, it will not be applied to the user.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = RoleDto.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@PostMapping("")
@PreAuthorize("hasAuthority(@DbSetup.USER_WRITE) and hasAuthority(@DbSetup.ROLE_READ)")
public List<RoleDto> setUserRoles(@PathVariable int userId, @RequestBody List<Integer> roleIds) {
User user = userService.get(userId);
user.setRoles(new HashSet<>(roleService.get(roleIds)));
userService.save(user);
return userService.getRoles(userId).stream()
.map(roleMapper::toDto)
.collect(Collectors.toList());
}
/**
* Add roles to user.
*
* @param userId User id
* @param roleIds Role ids
* @return {@link List} of {@link RoleDto}
* @apiNote Does not throw error if user already has specified role
* @apiNote Does not throw error if roles not found
*/
// region
@Operation(
summary = "Add User's Roles",
description = "Add list of roles applied to a user."
+ "<ul>"
+ "<li>If a role ID is not found in the system, it will not be applied to the user.</li>"
+ "<li>If a role ID is already applied to the user, it will remain applied.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = RoleDto.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@PutMapping("")
@PreAuthorize("""
hasAuthority(@DbSetup.USER_READ)
and hasAuthority(@DbSetup.USER_WRITE)
and hasAuthority(@DbSetup.ROLE_READ)
""")
public List<RoleDto> addUserRoles(@PathVariable int userId, @RequestBody List<Integer> roleIds) {
Set<Role> roles = userService.getRoles(userId);
roles.addAll(roleService.get(roleIds));
User user = userService.get(userId);
user.setRoles(roles);
userService.save(user);
return userService.getRoles(userId).stream()
.map(roleMapper::toDto)
.collect(Collectors.toList());
}
/**
* Delete roles from user.
*
* @param userId User id
* @param roleIds Role ids
* @return {@link List} of {@link RoleDto}
* @apiNote Does not throw error if user does not have specified role
*/
// region
@Operation(
summary = "Remove User's Roles",
description = "Remove list of roles applied to a user."
+ "<ul>"
+ "<li>If a role ID is not applied to the user, it will remain not applied.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = RoleDto.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@DeleteMapping("")
@PreAuthorize("""
hasAuthority(@DbSetup.USER_READ)
and hasAuthority(@DbSetup.USER_WRITE)
and hasAuthority(@DbSetup.ROLE_READ)
""")
public List<RoleDto> removeUserRoles(@PathVariable int userId, @RequestBody List<Integer> roleIds) {
Set<Role> roles = userService.getRoles(userId);
roles.removeAll(roleService.get(roleIds));
User user = userService.get(userId);
user.setRoles(roles);
userService.save(user);
return userService.getRoles(userId).stream()
.map(roleMapper::toDto)
.collect(Collectors.toList());
}
}