fix: null safety in role/group creation, add user create/update endpoints

- RoleAdminController.createRole: default null description to "" and null scope to "custom"
- RoleAdminController.updateRole: pass null audit details to avoid NPE when name is null
- GroupAdminController.updateGroup: pass null audit details to avoid NPE when name is null
- UserAdminController: add POST / createUser endpoint with default VIEWER role assignment
- UserAdminController: add PUT /{userId} updateUser endpoint for displayName/email updates
This commit is contained in:
hsiegeln
2026-03-17 18:49:34 +01:00
parent d025919f8d
commit f42e6279e6
3 changed files with 50 additions and 3 deletions

View File

@@ -111,7 +111,7 @@ public class GroupAdminController {
groupRepository.update(id, request.name(), request.parentGroupId());
auditService.log("update_group", AuditCategory.RBAC, id.toString(),
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
null, AuditResult.SUCCESS, httpRequest);
return ResponseEntity.ok().build();
}

View File

@@ -69,7 +69,9 @@ public class RoleAdminController {
@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());
String desc = request.description() != null ? request.description() : "";
String sc = request.scope() != null ? request.scope() : "custom";
UUID id = roleRepository.create(request.name(), desc, sc);
auditService.log("create_role", AuditCategory.RBAC, id.toString(),
Map.of("name", request.name()), AuditResult.SUCCESS, httpRequest);
return ResponseEntity.ok(Map.of("id", id));
@@ -93,7 +95,7 @@ public class RoleAdminController {
}
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);
null, AuditResult.SUCCESS, httpRequest);
return ResponseEntity.ok().build();
}

View File

@@ -4,7 +4,9 @@ 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.rbac.UserDetail;
import com.cameleer3.server.core.security.UserInfo;
import com.cameleer3.server.core.security.UserRepository;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -16,9 +18,12 @@ 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.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -63,6 +68,43 @@ public class UserAdminController {
return ResponseEntity.ok(detail);
}
@PostMapping
@Operation(summary = "Create a local user")
@ApiResponse(responseCode = "200", description = "User created")
public ResponseEntity<UserDetail> createUser(@RequestBody CreateUserRequest request,
HttpServletRequest httpRequest) {
String userId = "user:" + request.username();
UserInfo user = new UserInfo(userId, "local",
request.email() != null ? request.email() : "",
request.displayName() != null ? request.displayName() : request.username(),
Instant.now());
userRepository.upsert(user);
rbacService.assignRoleToUser(userId, SystemRole.VIEWER_ID);
auditService.log("create_user", AuditCategory.USER_MGMT, userId,
Map.of("username", request.username()), AuditResult.SUCCESS, httpRequest);
return ResponseEntity.ok(rbacService.getUser(userId));
}
@PutMapping("/{userId}")
@Operation(summary = "Update user display name or email")
@ApiResponse(responseCode = "200", description = "User updated")
@ApiResponse(responseCode = "404", description = "User not found")
public ResponseEntity<Void> updateUser(@PathVariable String userId,
@RequestBody UpdateUserRequest request,
HttpServletRequest httpRequest) {
var existing = userRepository.findById(userId);
if (existing.isEmpty()) return ResponseEntity.notFound().build();
var user = existing.get();
var updated = new UserInfo(user.userId(), user.provider(),
request.email() != null ? request.email() : user.email(),
request.displayName() != null ? request.displayName() : user.displayName(),
user.createdAt());
userRepository.upsert(updated);
auditService.log("update_user", AuditCategory.USER_MGMT, userId,
null, AuditResult.SUCCESS, httpRequest);
return ResponseEntity.ok().build();
}
@PostMapping("/{userId}/roles/{roleId}")
@Operation(summary = "Assign a role to a user")
@ApiResponse(responseCode = "200", description = "Role assigned")
@@ -122,4 +164,7 @@ public class UserAdminController {
null, AuditResult.SUCCESS, httpRequest);
return ResponseEntity.noContent().build();
}
public record CreateUserRequest(String username, String displayName, String email) {}
public record UpdateUserRequest(String displayName, String email) {}
}