feat: add Group, Role, and RBAC stats admin controllers
GroupAdminController with cycle detection, RoleAdminController with system role protection, RbacStatsController for dashboard. Rewrite UserAdminController to use RbacService.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
package com.cameleer3.server.app.controller;
|
||||
|
||||
import com.cameleer3.server.core.admin.AuditCategory;
|
||||
import com.cameleer3.server.core.admin.AuditResult;
|
||||
import com.cameleer3.server.core.admin.AuditService;
|
||||
import com.cameleer3.server.core.rbac.GroupDetail;
|
||||
import com.cameleer3.server.core.rbac.GroupRepository;
|
||||
import com.cameleer3.server.core.rbac.GroupSummary;
|
||||
import com.cameleer3.server.core.rbac.RbacService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Admin endpoints for group management.
|
||||
* Protected by {@code ROLE_ADMIN}.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/groups")
|
||||
@Tag(name = "Group Admin", description = "Group management (ADMIN only)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class GroupAdminController {
|
||||
|
||||
private final GroupRepository groupRepository;
|
||||
private final RbacService rbacService;
|
||||
private final AuditService auditService;
|
||||
|
||||
public GroupAdminController(GroupRepository groupRepository, RbacService rbacService,
|
||||
AuditService auditService) {
|
||||
this.groupRepository = groupRepository;
|
||||
this.rbacService = rbacService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List all groups with hierarchy and effective roles")
|
||||
@ApiResponse(responseCode = "200", description = "Group list returned")
|
||||
public ResponseEntity<List<GroupDetail>> listGroups() {
|
||||
List<GroupSummary> summaries = groupRepository.findAll();
|
||||
List<GroupDetail> details = new ArrayList<>();
|
||||
for (GroupSummary summary : summaries) {
|
||||
groupRepository.findById(summary.id()).ifPresent(details::add);
|
||||
}
|
||||
return ResponseEntity.ok(details);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get group by ID with effective roles")
|
||||
@ApiResponse(responseCode = "200", description = "Group found")
|
||||
@ApiResponse(responseCode = "404", description = "Group not found")
|
||||
public ResponseEntity<GroupDetail> getGroup(@PathVariable UUID id) {
|
||||
return groupRepository.findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Create a new group")
|
||||
@ApiResponse(responseCode = "200", description = "Group created")
|
||||
public ResponseEntity<Map<String, UUID>> createGroup(@RequestBody CreateGroupRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
UUID id = groupRepository.create(request.name(), request.parentGroupId());
|
||||
auditService.log("create_group", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok(Map.of("id", id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "Update group name or parent")
|
||||
@ApiResponse(responseCode = "200", description = "Group updated")
|
||||
@ApiResponse(responseCode = "404", description = "Group not found")
|
||||
@ApiResponse(responseCode = "409", description = "Cycle detected in group hierarchy")
|
||||
public ResponseEntity<Void> updateGroup(@PathVariable UUID id,
|
||||
@RequestBody UpdateGroupRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
Optional<GroupDetail> existing = groupRepository.findById(id);
|
||||
if (existing.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// Cycle detection: walk ancestor chain of proposed parent and check if it includes 'id'
|
||||
if (request.parentGroupId() != null) {
|
||||
List<GroupSummary> ancestors = groupRepository.findAncestorChain(request.parentGroupId());
|
||||
for (GroupSummary ancestor : ancestors) {
|
||||
if (ancestor.id().equals(id)) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
}
|
||||
// Also check that the proposed parent itself is not the group being updated
|
||||
if (request.parentGroupId().equals(id)) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
}
|
||||
|
||||
groupRepository.update(id, request.name(), request.parentGroupId());
|
||||
auditService.log("update_group", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete group")
|
||||
@ApiResponse(responseCode = "204", description = "Group deleted")
|
||||
@ApiResponse(responseCode = "404", description = "Group not found")
|
||||
public ResponseEntity<Void> deleteGroup(@PathVariable UUID id,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (groupRepository.findById(id).isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
groupRepository.delete(id);
|
||||
auditService.log("delete_group", AuditCategory.RBAC, id.toString(),
|
||||
null, AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/roles/{roleId}")
|
||||
@Operation(summary = "Assign a role to a group")
|
||||
@ApiResponse(responseCode = "200", description = "Role assigned to group")
|
||||
@ApiResponse(responseCode = "404", description = "Group not found")
|
||||
public ResponseEntity<Void> assignRoleToGroup(@PathVariable UUID id,
|
||||
@PathVariable UUID roleId,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (groupRepository.findById(id).isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
groupRepository.addRole(id, roleId);
|
||||
auditService.log("assign_role_to_group", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("roleId", roleId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}/roles/{roleId}")
|
||||
@Operation(summary = "Remove a role from a group")
|
||||
@ApiResponse(responseCode = "204", description = "Role removed from group")
|
||||
@ApiResponse(responseCode = "404", description = "Group not found")
|
||||
public ResponseEntity<Void> removeRoleFromGroup(@PathVariable UUID id,
|
||||
@PathVariable UUID roleId,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (groupRepository.findById(id).isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
groupRepository.removeRole(id, roleId);
|
||||
auditService.log("remove_role_from_group", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("roleId", roleId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
public record CreateGroupRequest(String name, UUID parentGroupId) {}
|
||||
public record UpdateGroupRequest(String name, UUID parentGroupId) {}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.cameleer3.server.app.controller;
|
||||
|
||||
import com.cameleer3.server.core.rbac.RbacService;
|
||||
import com.cameleer3.server.core.rbac.RbacStats;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Admin endpoint for RBAC statistics.
|
||||
* Protected by {@code ROLE_ADMIN}.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/rbac")
|
||||
@Tag(name = "RBAC Stats", description = "RBAC statistics (ADMIN only)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class RbacStatsController {
|
||||
|
||||
private final RbacService rbacService;
|
||||
|
||||
public RbacStatsController(RbacService rbacService) {
|
||||
this.rbacService = rbacService;
|
||||
}
|
||||
|
||||
@GetMapping("/stats")
|
||||
@Operation(summary = "Get RBAC statistics for the dashboard")
|
||||
@ApiResponse(responseCode = "200", description = "RBAC stats returned")
|
||||
public ResponseEntity<RbacStats> getStats() {
|
||||
return ResponseEntity.ok(rbacService.getStats());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.cameleer3.server.app.controller;
|
||||
|
||||
import com.cameleer3.server.core.admin.AuditCategory;
|
||||
import com.cameleer3.server.core.admin.AuditResult;
|
||||
import com.cameleer3.server.core.admin.AuditService;
|
||||
import com.cameleer3.server.core.rbac.RbacService;
|
||||
import com.cameleer3.server.core.rbac.RoleDetail;
|
||||
import com.cameleer3.server.core.rbac.RoleRepository;
|
||||
import com.cameleer3.server.core.rbac.SystemRole;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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 java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Admin endpoints for role management.
|
||||
* Protected by {@code ROLE_ADMIN}.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/roles")
|
||||
@Tag(name = "Role Admin", description = "Role management (ADMIN only)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class RoleAdminController {
|
||||
|
||||
private final RoleRepository roleRepository;
|
||||
private final RbacService rbacService;
|
||||
private final AuditService auditService;
|
||||
|
||||
public RoleAdminController(RoleRepository roleRepository, RbacService rbacService,
|
||||
AuditService auditService) {
|
||||
this.roleRepository = roleRepository;
|
||||
this.rbacService = rbacService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List all roles (system and custom)")
|
||||
@ApiResponse(responseCode = "200", description = "Role list returned")
|
||||
public ResponseEntity<List<RoleDetail>> listRoles() {
|
||||
return ResponseEntity.ok(roleRepository.findAll());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get role by ID with effective principals")
|
||||
@ApiResponse(responseCode = "200", description = "Role found")
|
||||
@ApiResponse(responseCode = "404", description = "Role not found")
|
||||
public ResponseEntity<RoleDetail> getRole(@PathVariable UUID id) {
|
||||
return roleRepository.findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Create a custom role")
|
||||
@ApiResponse(responseCode = "200", description = "Role created")
|
||||
public ResponseEntity<Map<String, UUID>> createRole(@RequestBody CreateRoleRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
UUID id = roleRepository.create(request.name(), request.description(), request.scope());
|
||||
auditService.log("create_role", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok(Map.of("id", id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "Update a custom role")
|
||||
@ApiResponse(responseCode = "200", description = "Role updated")
|
||||
@ApiResponse(responseCode = "403", description = "Cannot modify system role")
|
||||
@ApiResponse(responseCode = "404", description = "Role not found")
|
||||
public ResponseEntity<Void> updateRole(@PathVariable UUID id,
|
||||
@RequestBody UpdateRoleRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (SystemRole.isSystem(id)) {
|
||||
auditService.log("update_role", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("reason", "system_role_protected"), AuditResult.FAILURE, httpRequest);
|
||||
return ResponseEntity.status(403).build();
|
||||
}
|
||||
if (roleRepository.findById(id).isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
roleRepository.update(id, request.name(), request.description(), request.scope());
|
||||
auditService.log("update_role", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete a custom role")
|
||||
@ApiResponse(responseCode = "204", description = "Role deleted")
|
||||
@ApiResponse(responseCode = "403", description = "Cannot delete system role")
|
||||
@ApiResponse(responseCode = "404", description = "Role not found")
|
||||
public ResponseEntity<Void> deleteRole(@PathVariable UUID id,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (SystemRole.isSystem(id)) {
|
||||
auditService.log("delete_role", AuditCategory.RBAC, id.toString(),
|
||||
Map.of("reason", "system_role_protected"), AuditResult.FAILURE, httpRequest);
|
||||
return ResponseEntity.status(403).build();
|
||||
}
|
||||
if (roleRepository.findById(id).isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
roleRepository.delete(id);
|
||||
auditService.log("delete_role", AuditCategory.RBAC, id.toString(),
|
||||
null, AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
public record CreateRoleRequest(String name, String description, String scope) {}
|
||||
public record UpdateRoleRequest(String name, String description, String scope) {}
|
||||
}
|
||||
@@ -4,20 +4,18 @@ import com.cameleer3.server.core.admin.AuditCategory;
|
||||
import com.cameleer3.server.core.admin.AuditResult;
|
||||
import com.cameleer3.server.core.admin.AuditService;
|
||||
import com.cameleer3.server.core.rbac.RbacService;
|
||||
import com.cameleer3.server.core.rbac.SystemRole;
|
||||
import com.cameleer3.server.core.security.UserInfo;
|
||||
import com.cameleer3.server.core.rbac.UserDetail;
|
||||
import com.cameleer3.server.core.security.UserRepository;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -27,7 +25,7 @@ import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Admin endpoints for user management.
|
||||
* Protected by {@code ROLE_ADMIN} via SecurityConfig URL patterns.
|
||||
* Protected by {@code ROLE_ADMIN}.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/users")
|
||||
@@ -35,63 +33,85 @@ import java.util.UUID;
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class UserAdminController {
|
||||
|
||||
private final RbacService rbacService;
|
||||
private final UserRepository userRepository;
|
||||
private final AuditService auditService;
|
||||
private final RbacService rbacService;
|
||||
|
||||
public UserAdminController(UserRepository userRepository, AuditService auditService,
|
||||
RbacService rbacService) {
|
||||
public UserAdminController(RbacService rbacService, UserRepository userRepository,
|
||||
AuditService auditService) {
|
||||
this.rbacService = rbacService;
|
||||
this.userRepository = userRepository;
|
||||
this.auditService = auditService;
|
||||
this.rbacService = rbacService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List all users")
|
||||
@Operation(summary = "List all users with RBAC detail")
|
||||
@ApiResponse(responseCode = "200", description = "User list returned")
|
||||
public ResponseEntity<List<UserInfo>> listUsers() {
|
||||
return ResponseEntity.ok(userRepository.findAll());
|
||||
public ResponseEntity<List<UserDetail>> listUsers() {
|
||||
return ResponseEntity.ok(rbacService.listUsers());
|
||||
}
|
||||
|
||||
@GetMapping("/{userId}")
|
||||
@Operation(summary = "Get user by ID")
|
||||
@Operation(summary = "Get user by ID with RBAC detail")
|
||||
@ApiResponse(responseCode = "200", description = "User found")
|
||||
@ApiResponse(responseCode = "404", description = "User not found")
|
||||
public ResponseEntity<UserInfo> getUser(@PathVariable String userId) {
|
||||
return userRepository.findById(userId)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PutMapping("/{userId}/roles")
|
||||
@Operation(summary = "Update user roles")
|
||||
@ApiResponse(responseCode = "200", description = "Roles updated")
|
||||
@ApiResponse(responseCode = "404", description = "User not found")
|
||||
public ResponseEntity<Void> updateRoles(@PathVariable String userId,
|
||||
@RequestBody RolesRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
if (userRepository.findById(userId).isEmpty()) {
|
||||
public ResponseEntity<UserDetail> getUser(@PathVariable String userId) {
|
||||
UserDetail detail = rbacService.getUser(userId);
|
||||
if (detail == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
// Remove all existing direct roles, then assign the new ones
|
||||
List<String> currentRoles = rbacService.getSystemRoleNames(userId);
|
||||
for (String roleName : currentRoles) {
|
||||
UUID roleId = SystemRole.BY_NAME.get(roleName);
|
||||
if (roleId != null) {
|
||||
rbacService.removeRoleFromUser(userId, roleId);
|
||||
}
|
||||
}
|
||||
for (String roleName : request.roles()) {
|
||||
UUID roleId = SystemRole.BY_NAME.get(roleName.toUpperCase());
|
||||
if (roleId != null) {
|
||||
rbacService.assignRoleToUser(userId, roleId);
|
||||
}
|
||||
}
|
||||
auditService.log("update_roles", AuditCategory.USER_MGMT, userId,
|
||||
Map.of("roles", request.roles()), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok(detail);
|
||||
}
|
||||
|
||||
@PostMapping("/{userId}/roles/{roleId}")
|
||||
@Operation(summary = "Assign a role to a user")
|
||||
@ApiResponse(responseCode = "200", description = "Role assigned")
|
||||
@ApiResponse(responseCode = "404", description = "User or role not found")
|
||||
public ResponseEntity<Void> assignRoleToUser(@PathVariable String userId,
|
||||
@PathVariable UUID roleId,
|
||||
HttpServletRequest httpRequest) {
|
||||
rbacService.assignRoleToUser(userId, roleId);
|
||||
auditService.log("assign_role_to_user", AuditCategory.USER_MGMT, userId,
|
||||
Map.of("roleId", roleId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}/roles/{roleId}")
|
||||
@Operation(summary = "Remove a role from a user")
|
||||
@ApiResponse(responseCode = "204", description = "Role removed")
|
||||
public ResponseEntity<Void> removeRoleFromUser(@PathVariable String userId,
|
||||
@PathVariable UUID roleId,
|
||||
HttpServletRequest httpRequest) {
|
||||
rbacService.removeRoleFromUser(userId, roleId);
|
||||
auditService.log("remove_role_from_user", AuditCategory.USER_MGMT, userId,
|
||||
Map.of("roleId", roleId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{userId}/groups/{groupId}")
|
||||
@Operation(summary = "Add a user to a group")
|
||||
@ApiResponse(responseCode = "200", description = "User added to group")
|
||||
public ResponseEntity<Void> addUserToGroup(@PathVariable String userId,
|
||||
@PathVariable UUID groupId,
|
||||
HttpServletRequest httpRequest) {
|
||||
rbacService.addUserToGroup(userId, groupId);
|
||||
auditService.log("add_user_to_group", AuditCategory.USER_MGMT, userId,
|
||||
Map.of("groupId", groupId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}/groups/{groupId}")
|
||||
@Operation(summary = "Remove a user from a group")
|
||||
@ApiResponse(responseCode = "204", description = "User removed from group")
|
||||
public ResponseEntity<Void> removeUserFromGroup(@PathVariable String userId,
|
||||
@PathVariable UUID groupId,
|
||||
HttpServletRequest httpRequest) {
|
||||
rbacService.removeUserFromGroup(userId, groupId);
|
||||
auditService.log("remove_user_from_group", AuditCategory.USER_MGMT, userId,
|
||||
Map.of("groupId", groupId), AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}")
|
||||
@Operation(summary = "Delete user")
|
||||
@ApiResponse(responseCode = "204", description = "User deleted")
|
||||
@@ -102,6 +122,4 @@ public class UserAdminController {
|
||||
null, AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
public record RolesRequest(List<String> roles) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user