Allow ADMIN users to bulk deactivate and reactivate by role.

Platform ADMIN callers no longer hit 403 on these endpoints; bulk changes to platform users.role ADMIN remain restricted to SUPER_ADMIN, while team_members.team_role ADMIN is still eligible under path ADMIN.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-05-19 01:09:42 -07:00
parent ecad91d89e
commit 2f73b60122
4 changed files with 35 additions and 21 deletions

View File

@ -512,7 +512,7 @@ const docTemplate = `{
"Bearer": []
}
],
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
"consumes": [
"application/json"
],
@ -522,7 +522,7 @@ const docTemplate = `{
"tags": [
"admin"
],
"summary": "Bulk deactivate accounts by role (SUPER_ADMIN only)",
"summary": "Bulk deactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)",
"parameters": [
{
"type": "string",
@ -575,7 +575,7 @@ const docTemplate = `{
"Bearer": []
}
],
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. Matches only users currently DEACTIVATED and team rows currently inactive.",
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
"consumes": [
"application/json"
],
@ -585,7 +585,7 @@ const docTemplate = `{
"tags": [
"admin"
],
"summary": "Bulk reactivate accounts by role (SUPER_ADMIN only)",
"summary": "Bulk reactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)",
"parameters": [
{
"type": "string",

View File

@ -504,7 +504,7 @@
"Bearer": []
}
],
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
"consumes": [
"application/json"
],
@ -514,7 +514,7 @@
"tags": [
"admin"
],
"summary": "Bulk deactivate accounts by role (SUPER_ADMIN only)",
"summary": "Bulk deactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)",
"parameters": [
{
"type": "string",
@ -567,7 +567,7 @@
"Bearer": []
}
],
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. Matches only users currently DEACTIVATED and team rows currently inactive.",
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
"consumes": [
"application/json"
],
@ -577,7 +577,7 @@
"tags": [
"admin"
],
"summary": "Bulk reactivate accounts by role (SUPER_ADMIN only)",
"summary": "Bulk reactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)",
"parameters": [
{
"type": "string",

View File

@ -2881,6 +2881,8 @@ paths:
(except the caller) and all team_members with the given team_role to inactive.
The path role must match a valid learner role or team role string (e.g. STUDENT,
INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated.
ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN
users (team_members with team_role ADMIN under path ADMIN remain allowed).
Empty body allowed; optionally pass exclude_team_member_id to skip one team_members
row (e.g. yourself).
parameters:
@ -2915,7 +2917,8 @@ paths:
$ref: '#/definitions/domain.ErrorResponse'
security:
- Bearer: []
summary: Bulk deactivate accounts by role (SUPER_ADMIN only)
summary: Bulk deactivate accounts by role (SUPER_ADMIN or ADMIN platform users
only)
tags:
- admin
/api/v1/admin/roles/{role}/bulk-reactivate:
@ -2925,8 +2928,9 @@ paths:
description: Sets all platform users with the given role from DEACTIVATED to
ACTIVE (except the caller) and all team_members with the given team_role from
inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN
cannot be bulk changed. Matches only users currently DEACTIVATED and team
rows currently inactive.
cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN
users (team_members ADMIN under path ADMIN is allowed). Matches only users
currently DEACTIVATED and team rows currently inactive.
parameters:
- description: Role key (matches users.role and/or team_members.team_role)
in: path
@ -2959,7 +2963,8 @@ paths:
$ref: '#/definitions/domain.ErrorResponse'
security:
- Bearer: []
summary: Bulk reactivate accounts by role (SUPER_ADMIN only)
summary: Bulk reactivate accounts by role (SUPER_ADMIN or ADMIN platform users
only)
tags:
- admin
/api/v1/admin/users/{user_id}/lms-learning-activity:

View File

@ -382,8 +382,8 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
}
// BulkDeactivateAccountsByRole godoc
// @Summary Bulk deactivate accounts by role (SUPER_ADMIN only)
// @Description Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).
// @Summary Bulk deactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)
// @Description Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).
// @Tags admin
// @Accept json
// @Produce json
@ -403,10 +403,10 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
Error: "role not found in context",
})
}
if callerRole != domain.RoleSuperAdmin {
if callerRole != domain.RoleSuperAdmin && callerRole != domain.RoleAdmin {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: "Forbidden",
Error: "only SUPER_ADMIN platform users may bulk deactivate by role",
Error: "only SUPER_ADMIN or ADMIN platform users may bulk deactivate by role",
})
}
@ -431,7 +431,6 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
Error: "SUPER_ADMIN cannot be bulk deactivated",
})
}
validUserRole := domain.Role(roleKey).IsValid()
validTeamRole := domain.TeamRole(roleKey).IsValid()
if !validUserRole && !validTeamRole {
@ -441,6 +440,11 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
})
}
// Non-super-admins cannot bulk change other platform ADMIN users (same role); team_members ADMIN is still allowed.
if callerRole != domain.RoleSuperAdmin && roleKey == string(domain.RoleAdmin) {
validUserRole = false
}
var req domain.BulkAccountsByRoleRequest
if len(c.Body()) > 0 {
if err := c.BodyParser(&req); err != nil {
@ -504,8 +508,8 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
}
// BulkReactivateAccountsByRole godoc
// @Summary Bulk reactivate accounts by role (SUPER_ADMIN only)
// @Description Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. Matches only users currently DEACTIVATED and team rows currently inactive.
// @Summary Bulk reactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)
// @Description Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.
// @Tags admin
// @Accept json
// @Produce json
@ -525,10 +529,10 @@ func (h *Handler) BulkReactivateAccountsByRole(c *fiber.Ctx) error {
Error: "role not found in context",
})
}
if callerRole != domain.RoleSuperAdmin {
if callerRole != domain.RoleSuperAdmin && callerRole != domain.RoleAdmin {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: "Forbidden",
Error: "only SUPER_ADMIN platform users may bulk reactivate by role",
Error: "only SUPER_ADMIN or ADMIN platform users may bulk reactivate by role",
})
}
@ -563,6 +567,11 @@ func (h *Handler) BulkReactivateAccountsByRole(c *fiber.Ctx) error {
})
}
// Non-super-admins cannot bulk change other platform ADMIN users; team_members ADMIN is still allowed.
if callerRole != domain.RoleSuperAdmin && roleKey == string(domain.RoleAdmin) {
validUserRole = false
}
var req domain.BulkAccountsByRoleRequest
if len(c.Body()) > 0 {
if err := c.BodyParser(&req); err != nil {