Merged in jmathison/DEVOPS-722 (pull request #226)

DEVOPS-722 enforce team management visibility

* DEVOPS-722 enforce team management visibility

* DEVOPS-722 reduce admin user list complexity

* DEVOPS-722 cover admin access helpers

* DEVOPS-722 address team management PR feedback

* DEVOPS-722 fix admin access tests in CI

* DEVOPS-722 fix remaining admin PR feedback
This commit is contained in:
Jacob Mathison
2026-05-12 16:49:12 +00:00
parent a080ca59d8
commit e3d9047143
10 changed files with 1667 additions and 528 deletions
+168 -47
View File
@@ -18,6 +18,12 @@ import (
"github.com/oapi-codegen/runtime/types" "github.com/oapi-codegen/runtime/types"
) )
const (
adminCognitoListPageSize = 60
maxAdminCognitoListPages = 100
maxAdminCognitoListUsers = 6000
)
// getAdminUserForAudit retrieves the admin user information for audit logging. // getAdminUserForAudit retrieves the admin user information for audit logging.
// When DISABLE_AUTH is set to "true", it returns a default system user for audit purposes. // When DISABLE_AUTH is set to "true", it returns a default system user for audit purposes.
// Otherwise, it attempts to extract user info from the JWT token in the request. // Otherwise, it attempts to extract user info from the JWT token in the request.
@@ -95,6 +101,12 @@ func (ctrl *Controllers) CreateAdminUser(ctx echo.Context) error {
}) })
} }
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
if !canAdminModeCreateUser(ctrl.getAdminTeamMode(ctx, httpClient, permitConfig), string(req.Email), req.Roles) {
return adminUserForbidden(ctx)
}
// Create AWS config and Cognito client // Create AWS config and Cognito client
awsCfg, err := aws.GetAWSConfig(context.Background()) awsCfg, err := aws.GetAWSConfig(context.Background())
if err != nil { if err != nil {
@@ -204,9 +216,6 @@ func (ctrl *Controllers) CreateAdminUser(ctx echo.Context) error {
) )
// Create user in Permit.io // Create user in Permit.io
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
permitSynced := false permitSynced := false
err = usermanagement.CreatePermitUser(httpClient, permitConfig, cognitoUser) err = usermanagement.CreatePermitUser(httpClient, permitConfig, cognitoUser)
if err != nil { if err != nil {
@@ -354,14 +363,11 @@ func (ctrl *Controllers) GetAdminUser(ctx echo.Context, email UserEmail) error {
}) })
} }
// Get user roles from Permit.io targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
httpClient := &http.Client{Timeout: 10 * time.Second} if !targetAccess.canRead(cognitoUser.Email) {
permitConfig := ctrl.getPermitConfig() return adminUserNotFound(ctx, string(email))
roles, err := usermanagement.GetUserRoles(httpClient, permitConfig, cognitoUser.SubjectID)
if err != nil {
ctrl.cfg.GetAuthLogger().Warn("Failed to get user roles from Permit.io", "error", err, "email", email)
} }
roles := targetAccess.roles
// Log successful retrieval // Log successful retrieval
usermanagement.LogUserListAction( usermanagement.LogUserListAction(
@@ -422,6 +428,11 @@ func (ctrl *Controllers) CheckAdminUserExists(ctx echo.Context, email UserEmail)
return ctx.NoContent(http.StatusNotFound) return ctx.NoContent(http.StatusNotFound)
} }
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canRead(cognitoUser.Email) {
return ctx.NoContent(http.StatusNotFound)
}
return ctx.NoContent(http.StatusOK) return ctx.NoContent(http.StatusOK)
} }
@@ -453,43 +464,34 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
Region: ctrl.cfg.GetAuthRegion(), Region: ctrl.cfg.GetAuthRegion(),
} }
// Set default pagination parameters page, pageSize := adminUserListPagination(params)
page := int32(1)
pageSize := int32(50)
if params.Page != nil && *params.Page > 0 {
page = *params.Page
}
if params.PageSize != nil && *params.PageSize > 0 && *params.PageSize <= 60 {
pageSize = *params.PageSize
}
// List users from Cognito (note: Cognito doesn't support traditional page numbers, only pagination tokens) allUsers, err := listAllAdminCognitoUsers(cognitoClient, cognitoConfig.UserPoolID)
// For simplicity, we'll just return the first page
result, err := usermanagement.ListCognitoUsers(context.Background(), cognitoClient, cognitoConfig.UserPoolID, int(pageSize), nil)
if err != nil { if err != nil {
ctrl.cfg.GetAuthLogger().Error("Failed to list users from Cognito", "error", err) ctrl.cfg.GetAuthLogger().Error("Failed to list users from Cognito", "error", err)
return ctx.JSON(http.StatusBadGateway, ErrorMessage{ return ctx.JSON(http.StatusBadGateway, ErrorMessage{
Message: fmt.Sprintf("Failed to list users from Cognito: %v", err), Message: fmt.Sprintf("Failed to list users from Cognito: %v", err),
}) })
} }
filteredUsers := usermanagement.FilterUsersByEnvironmentID(allUsers, ctrl.getCognitoEnvironmentID())
// Apply environment filtering when COGNITO_ENVIRONMENT_ID is configured httpClient := &http.Client{Timeout: 10 * time.Second}
filteredUsers := usermanagement.FilterUsersByEnvironmentID(result.Users, ctrl.getCognitoEnvironmentID()) permitConfig := ctrl.getPermitConfig()
adminMode := ctrl.getAdminTeamMode(ctx, httpClient, permitConfig)
// Convert to AdminUserDetails format if adminMode == adminTeamModeNone {
users := make([]AdminUserDetails, 0, len(filteredUsers)) return adminUserForbidden(ctx)
for _, cognitoUser := range filteredUsers {
userDetail := AdminUserDetails{
CognitoSubjectId: cognitoUser.SubjectID,
Email: types.Email(cognitoUser.Email),
FirstName: &cognitoUser.FirstName,
LastName: &cognitoUser.LastName,
Status: cognitoUser.Status,
Enabled: cognitoUser.Enabled,
}
users = append(users, userDetail)
} }
roleAssignments, err := usermanagement.ListAllPermitRoleAssignments(httpClient, permitConfig)
if err != nil {
ctrl.cfg.GetAuthLogger().Error("Failed to list role assignments from Permit.io", "error", err)
return ctx.JSON(http.StatusBadGateway, ErrorMessage{
Message: fmt.Sprintf("Failed to list role assignments from Permit.io: %v", err),
})
}
visibleUsers := ctrl.visibleAdminUsers(filteredUsers, adminMode, adminRoleAssignmentsByUser(roleAssignments))
users, hasMore := paginateAdminUsers(visibleUsers, page, pageSize)
// Log list action // Log list action
usermanagement.LogUserListAction( usermanagement.LogUserListAction(
ctrl.cfg.GetAuthLogger(), ctrl.cfg.GetAuthLogger(),
@@ -500,9 +502,7 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
) )
// Build response // Build response
hasMore := result.PaginationKey != nil && *result.PaginationKey != "" total := int32(len(visibleUsers)) // #nosec G115 -- admin user lists are bounded by Cognito user pool size
// len(users) is bounded by pageSize (max 60), so this conversion is safe
total := int32(len(users)) // #nosec G115 -- len(users) is bounded by pageSize which is max 60
response := AdminUserList{ response := AdminUserList{
Users: users, Users: users,
Total: total, Total: total,
@@ -514,6 +514,106 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
return ctx.JSON(http.StatusOK, response) return ctx.JSON(http.StatusOK, response)
} }
func adminUserListPagination(params ListAdminUsersParams) (int32, int32) {
page := int32(1)
pageSize := int32(50)
if params.Page != nil && *params.Page > 0 {
page = *params.Page
}
if params.PageSize != nil && *params.PageSize > 0 && *params.PageSize <= 60 {
pageSize = *params.PageSize
}
return page, pageSize
}
func listAllAdminCognitoUsers(cognitoClient *cognitoidentityprovider.Client, userPoolID string) ([]usermanagement.CognitoUserResponse, error) {
return collectAdminCognitoUsers(func(pageSize int, paginationToken *string) (*usermanagement.ListCognitoUsersResult, error) {
return usermanagement.ListCognitoUsers(context.Background(), cognitoClient, userPoolID, pageSize, paginationToken)
})
}
func collectAdminCognitoUsers(fetchPage func(pageSize int, paginationToken *string) (*usermanagement.ListCognitoUsersResult, error)) ([]usermanagement.CognitoUserResponse, error) {
var users []usermanagement.CognitoUserResponse
var paginationToken *string
for page := 1; page <= maxAdminCognitoListPages; page++ {
result, err := fetchPage(adminCognitoListPageSize, paginationToken)
if err != nil {
return nil, err
}
users = append(users, result.Users...)
if len(users) > maxAdminCognitoListUsers {
return nil, fmt.Errorf("admin Cognito user listing exceeded %d users", maxAdminCognitoListUsers)
}
if result.PaginationKey == nil || *result.PaginationKey == "" {
return users, nil
}
paginationToken = result.PaginationKey
}
return nil, fmt.Errorf("admin Cognito user listing exceeded %d pages", maxAdminCognitoListPages)
}
func (ctrl *Controllers) visibleAdminUsers(users []usermanagement.CognitoUserResponse, mode adminTeamMode, rolesBySubject map[string][]string) []AdminUserDetails {
visibleUsers := make([]AdminUserDetails, 0, len(users))
for _, cognitoUser := range users {
roles, ok := rolesForAdminListUser(cognitoUser, mode, rolesBySubject)
if !ok || !canAdminModeReadTarget(mode, cognitoUser.Email, roles) {
continue
}
visibleUsers = append(visibleUsers, adminUserDetailsFromCognito(cognitoUser, roles))
}
return visibleUsers
}
func rolesForAdminListUser(cognitoUser usermanagement.CognitoUserResponse, mode adminTeamMode, rolesBySubject map[string][]string) ([]string, bool) {
roles, ok := rolesBySubject[cognitoUser.SubjectID]
if !ok && mode == adminTeamModeUserAdmin {
return nil, false
}
if roles == nil {
roles = []string{}
}
return roles, true
}
func adminRoleAssignmentsByUser(assignments []usermanagement.RoleAssignment) map[string][]string {
rolesBySubject := make(map[string][]string, len(assignments))
for _, assignment := range assignments {
if assignment.User == "" || assignment.Role == "" {
continue
}
rolesBySubject[assignment.User] = append(rolesBySubject[assignment.User], assignment.Role)
}
return rolesBySubject
}
func adminUserDetailsFromCognito(cognitoUser usermanagement.CognitoUserResponse, roles []string) AdminUserDetails {
if roles == nil {
roles = []string{}
}
userDetail := AdminUserDetails{
CognitoSubjectId: cognitoUser.SubjectID,
Email: types.Email(cognitoUser.Email),
FirstName: &cognitoUser.FirstName,
LastName: &cognitoUser.LastName,
Status: cognitoUser.Status,
Enabled: cognitoUser.Enabled,
}
userDetail.Roles = &roles
return userDetail
}
func paginateAdminUsers(visibleUsers []AdminUserDetails, page int32, pageSize int32) ([]AdminUserDetails, bool) {
start := int((page - 1) * pageSize) // #nosec G115 -- page and pageSize are positive int32 request parameters
end := start + int(pageSize)
if start > len(visibleUsers) {
start = len(visibleUsers)
}
if end > len(visibleUsers) {
end = len(visibleUsers)
}
return visibleUsers[start:end], end < len(visibleUsers)
}
// updateUserAttributesInCognito updates user attributes in Cognito and logs the operation. // updateUserAttributesInCognito updates user attributes in Cognito and logs the operation.
func (ctrl *Controllers) updateUserAttributesInCognito(ctx echo.Context, cognitoClient *cognitoidentityprovider.Client, cognitoConfig *usermanagement.CognitoConfig, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, cognitoUser *usermanagement.CognitoUserResponse) error { func (ctrl *Controllers) updateUserAttributesInCognito(ctx echo.Context, cognitoClient *cognitoidentityprovider.Client, cognitoConfig *usermanagement.CognitoConfig, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, cognitoUser *usermanagement.CognitoUserResponse) error {
if req.FirstName == nil && req.LastName == nil { if req.FirstName == nil && req.LastName == nil {
@@ -576,7 +676,7 @@ func (ctrl *Controllers) updateUserAttributesInCognito(ctx echo.Context, cognito
// updateUserRolesInPermit updates user roles in Permit.io and logs each operation. // updateUserRolesInPermit updates user roles in Permit.io and logs each operation.
func (ctrl *Controllers) updateUserRolesInPermit(ctx echo.Context, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, subjectID string, httpClient *http.Client, permitConfig *usermanagement.PermitConfig) { func (ctrl *Controllers) updateUserRolesInPermit(ctx echo.Context, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, subjectID string, httpClient *http.Client, permitConfig *usermanagement.PermitConfig) {
if req.Roles == nil || len(*req.Roles) == 0 { if req.Roles == nil {
return return
} }
@@ -733,6 +833,16 @@ func (ctrl *Controllers) UpdateAdminUser(ctx echo.Context, email UserEmail) erro
}) })
} }
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canMutate(cognitoUser.Email) {
return adminUserForbidden(ctx)
}
if !canAdminModeAssignRequestedRoles(targetAccess.mode, req.Roles) {
return adminUserForbidden(ctx)
}
httpClient := targetAccess.httpClient
permitConfig := targetAccess.permitConfig
// Update user attributes in Cognito if provided // Update user attributes in Cognito if provided
err = ctrl.updateUserAttributesInCognito(ctx, cognitoClient, cognitoConfig, email, req, adminUser, cognitoUser) err = ctrl.updateUserAttributesInCognito(ctx, cognitoClient, cognitoConfig, email, req, adminUser, cognitoUser)
if err != nil { if err != nil {
@@ -742,9 +852,6 @@ func (ctrl *Controllers) UpdateAdminUser(ctx echo.Context, email UserEmail) erro
} }
// Handle role updates in Permit.io if provided // Handle role updates in Permit.io if provided
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
ctrl.updateUserRolesInPermit(ctx, email, req, adminUser, cognitoUser.SubjectID, httpClient, permitConfig) ctrl.updateUserRolesInPermit(ctx, email, req, adminUser, cognitoUser.SubjectID, httpClient, permitConfig)
// Get updated user from Cognito // Get updated user from Cognito
@@ -847,6 +954,13 @@ func (ctrl *Controllers) DeleteAdminUser(ctx echo.Context, email UserEmail, para
}) })
} }
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canMutate(cognitoUser.Email) {
return adminUserForbidden(ctx)
}
httpClient := targetAccess.httpClient
permitConfig := targetAccess.permitConfig
// Delete user from Cognito // Delete user from Cognito
cognitoDeleted := false cognitoDeleted := false
err = usermanagement.DeleteCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) err = usermanagement.DeleteCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email))
@@ -879,9 +993,6 @@ func (ctrl *Controllers) DeleteAdminUser(ctx echo.Context, email UserEmail, para
} }
// Delete user from Permit.io // Delete user from Permit.io
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
permitDeleted := false permitDeleted := false
err = usermanagement.DeletePermitUser(httpClient, permitConfig, cognitoUser.SubjectID) err = usermanagement.DeletePermitUser(httpClient, permitConfig, cognitoUser.SubjectID)
if err != nil { if err != nil {
@@ -988,6 +1099,11 @@ func (ctrl *Controllers) DisableAdminUser(ctx echo.Context, email UserEmail) err
}) })
} }
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canMutate(cognitoUser.Email) {
return adminUserForbidden(ctx)
}
// Disable user in Cognito // Disable user in Cognito
err = usermanagement.DisableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) err = usermanagement.DisableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email))
if err != nil { if err != nil {
@@ -1099,6 +1215,11 @@ func (ctrl *Controllers) EnableAdminUser(ctx echo.Context, email UserEmail) erro
}) })
} }
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canMutate(cognitoUser.Email) {
return adminUserForbidden(ctx)
}
// Enable user in Cognito // Enable user in Cognito
err = usermanagement.EnableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) err = usermanagement.EnableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email))
if err != nil { if err != nil {
+195
View File
@@ -0,0 +1,195 @@
package queryapi
import (
"net/http"
"os"
"strings"
"time"
"queryorchestration/internal/cognitoauth"
"queryorchestration/internal/usermanagement"
"github.com/labstack/echo/v4"
)
type adminTargetAccess struct {
httpClient *http.Client
permitConfig *usermanagement.PermitConfig
mode adminTeamMode
roles []string
roleLookupOK bool
}
type adminTeamMode string
const (
adminTeamModeNone adminTeamMode = "none"
adminTeamModeSuperAdmin adminTeamMode = "super_admin"
adminTeamModeUserAdmin adminTeamMode = "user_admin"
adminTeamModeAuditor adminTeamMode = "auditor"
aareteEmailDomain = "aarete.com"
)
var rolesHiddenFromUserAdmin = map[string]struct{}{
"auditor": {},
"super_admin": {},
}
var rolesAssignableByUserAdmin = map[string]struct{}{
"client_user": {},
"user_admin": {},
}
func adminModeForRoles(roles []string) adminTeamMode {
if len(roles) == 0 {
return adminTeamModeNone
}
for _, role := range roles {
if role == "super_admin" {
return adminTeamModeSuperAdmin
}
}
for _, role := range roles {
if role == "user_admin" || role == "client_admin" {
return adminTeamModeUserAdmin
}
}
for _, role := range roles {
if role == "auditor" {
return adminTeamModeAuditor
}
}
return adminTeamModeNone
}
func isAAreteEmail(email string) bool {
parts := strings.Split(strings.TrimSpace(email), "@")
return len(parts) == 2 && strings.EqualFold(parts[1], aareteEmailDomain)
}
func userAdminCanSeeOrMutateTarget(email string, roles []string) bool {
if isAAreteEmail(email) {
return false
}
for _, role := range roles {
if _, hidden := rolesHiddenFromUserAdmin[role]; hidden {
return false
}
}
return true
}
func canAdminModeReadTarget(mode adminTeamMode, email string, roles []string) bool {
switch mode {
case adminTeamModeSuperAdmin, adminTeamModeAuditor:
return true
case adminTeamModeUserAdmin:
return userAdminCanSeeOrMutateTarget(email, roles)
default:
return false
}
}
func canAdminModeMutateTarget(mode adminTeamMode, email string, roles []string) bool {
switch mode {
case adminTeamModeSuperAdmin:
return true
case adminTeamModeUserAdmin:
return userAdminCanSeeOrMutateTarget(email, roles)
default:
return false
}
}
func userAdminCanAssignRoles(roles []string) bool {
for _, role := range roles {
if _, allowed := rolesAssignableByUserAdmin[role]; !allowed {
return false
}
}
return true
}
func canAdminModeCreateUser(mode adminTeamMode, email string, roles []string) bool {
switch mode {
case adminTeamModeSuperAdmin:
return true
case adminTeamModeUserAdmin:
return !isAAreteEmail(email) && userAdminCanAssignRoles(roles)
default:
return false
}
}
func canAdminModeAssignRequestedRoles(mode adminTeamMode, roles *[]string) bool {
return roles == nil || mode != adminTeamModeUserAdmin || userAdminCanAssignRoles(*roles)
}
func (ctrl *Controllers) getAdminTargetAccess(ctx echo.Context, email UserEmail, cognitoUser *usermanagement.CognitoUserResponse) adminTargetAccess {
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
roles, err := usermanagement.GetUserRoles(httpClient, permitConfig, cognitoUser.SubjectID)
roleLookupOK := err == nil
if err != nil {
ctrl.cfg.GetAuthLogger().Warn("Failed to get user roles from Permit.io", "error", err, "email", email)
}
return adminTargetAccess{
httpClient: httpClient,
permitConfig: permitConfig,
mode: ctrl.getAdminTeamMode(ctx, httpClient, permitConfig),
roles: roles,
roleLookupOK: roleLookupOK,
}
}
func (access adminTargetAccess) canRead(email string) bool {
if access.mode == adminTeamModeUserAdmin && !access.roleLookupOK {
return false
}
return canAdminModeReadTarget(access.mode, email, access.roles)
}
func (access adminTargetAccess) canMutate(email string) bool {
if access.mode == adminTeamModeUserAdmin && !access.roleLookupOK {
return false
}
return canAdminModeMutateTarget(access.mode, email, access.roles)
}
func (ctrl *Controllers) getAdminTeamMode(ctx echo.Context, httpClient *http.Client, permitConfig *usermanagement.PermitConfig) adminTeamMode {
if strings.EqualFold(os.Getenv("DISABLE_AUTH"), "true") {
return adminTeamModeSuperAdmin
}
if subjectID, ok := cognitoauth.GetUserSubject(ctx); ok && subjectID != "" {
roles, err := usermanagement.GetUserRoles(httpClient, permitConfig, subjectID)
if err == nil {
return adminModeForRoles(roles)
}
ctrl.cfg.GetAuthLogger().Warn("Failed to get admin roles from Permit.io", "error", err, "subject_id", subjectID)
}
if userInfo, ok := cognitoauth.GetUserInfo(ctx); ok {
return adminModeForRoles(userInfo.Groups)
}
return adminTeamModeNone
}
func adminUserForbidden(ctx echo.Context) error {
return ctx.JSON(http.StatusForbidden, ErrorMessage{
Message: "Insufficient permissions for this team management action",
})
}
func adminUserNotFound(ctx echo.Context, email string) error {
return ctx.JSON(http.StatusNotFound, ErrorMessage{
Message: "User not found: " + email,
})
}
+632
View File
@@ -0,0 +1,632 @@
package queryapi
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"queryorchestration/internal/cognitoauth"
"queryorchestration/internal/usermanagement"
"github.com/labstack/echo/v4"
)
func TestAdminModeForRoles(t *testing.T) {
tests := []struct {
name string
roles []string
want adminTeamMode
}{
{name: "super admin wins", roles: []string{"user_admin", "super_admin"}, want: adminTeamModeSuperAdmin},
{name: "client admin maps to user admin", roles: []string{"client_admin"}, want: adminTeamModeUserAdmin},
{name: "auditor without higher role", roles: []string{"auditor"}, want: adminTeamModeAuditor},
{name: "unrecognized", roles: []string{"client_user"}, want: adminTeamModeNone},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := adminModeForRoles(tt.roles); got != tt.want {
t.Fatalf("adminModeForRoles(%v) = %s, want %s", tt.roles, got, tt.want)
}
})
}
}
func TestIsAAreteEmail(t *testing.T) {
tests := []struct {
email string
want bool
}{
{email: "person@aarete.com", want: true},
{email: "Person@AArete.com", want: true},
{email: "person@sub.aarete.com", want: false},
{email: "person@aarete.com.fake", want: false},
{email: "not-an-email", want: false},
}
for _, tt := range tests {
t.Run(tt.email, func(t *testing.T) {
if got := isAAreteEmail(tt.email); got != tt.want {
t.Fatalf("isAAreteEmail(%q) = %v, want %v", tt.email, got, tt.want)
}
})
}
}
func TestAdminTargetVisibilityAndMutation(t *testing.T) {
clientRoles := []string{"client_user"}
userAdminRoles := []string{"user_admin"}
auditorRoles := []string{"auditor"}
superAdminRoles := []string{"super_admin"}
tests := []struct {
name string
mode adminTeamMode
email string
roles []string
wantRead bool
wantMutate bool
}{
{name: "user admin can read and mutate client user", mode: adminTeamModeUserAdmin, email: "client@example.com", roles: clientRoles, wantRead: true, wantMutate: true},
{name: "user admin can read and mutate user admin", mode: adminTeamModeUserAdmin, email: "admin@example.com", roles: userAdminRoles, wantRead: true, wantMutate: true},
{name: "user admin cannot read or mutate aarete domain", mode: adminTeamModeUserAdmin, email: "person@aarete.com", roles: clientRoles, wantRead: false, wantMutate: false},
{name: "user admin cannot read or mutate auditor", mode: adminTeamModeUserAdmin, email: "auditor@example.com", roles: auditorRoles, wantRead: false, wantMutate: false},
{name: "user admin cannot read or mutate super admin", mode: adminTeamModeUserAdmin, email: "super@example.com", roles: superAdminRoles, wantRead: false, wantMutate: false},
{name: "auditor can read all", mode: adminTeamModeAuditor, email: "person@aarete.com", roles: superAdminRoles, wantRead: true, wantMutate: false},
{name: "super admin can read and mutate all", mode: adminTeamModeSuperAdmin, email: "person@aarete.com", roles: superAdminRoles, wantRead: true, wantMutate: true},
{name: "none cannot read or mutate", mode: adminTeamModeNone, email: "client@example.com", roles: clientRoles, wantRead: false, wantMutate: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := canAdminModeReadTarget(tt.mode, tt.email, tt.roles); got != tt.wantRead {
t.Fatalf("canAdminModeReadTarget() = %v, want %v", got, tt.wantRead)
}
if got := canAdminModeMutateTarget(tt.mode, tt.email, tt.roles); got != tt.wantMutate {
t.Fatalf("canAdminModeMutateTarget() = %v, want %v", got, tt.wantMutate)
}
})
}
}
func TestAdminCreatePermissions(t *testing.T) {
tests := []struct {
name string
mode adminTeamMode
email string
roles []string
want bool
}{
{name: "super admin can create all roles", mode: adminTeamModeSuperAdmin, email: "person@aarete.com", roles: []string{"super_admin"}, want: true},
{name: "user admin can create client user", mode: adminTeamModeUserAdmin, email: "client@example.com", roles: []string{"client_user"}, want: true},
{name: "user admin can create user admin", mode: adminTeamModeUserAdmin, email: "admin@example.com", roles: []string{"client_user", "user_admin"}, want: true},
{name: "user admin cannot create aarete domain", mode: adminTeamModeUserAdmin, email: "person@aarete.com", roles: []string{"client_user"}, want: false},
{name: "user admin cannot create auditor", mode: adminTeamModeUserAdmin, email: "auditor@example.com", roles: []string{"auditor"}, want: false},
{name: "auditor cannot create", mode: adminTeamModeAuditor, email: "client@example.com", roles: []string{"client_user"}, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := canAdminModeCreateUser(tt.mode, tt.email, tt.roles); got != tt.want {
t.Fatalf("canAdminModeCreateUser() = %v, want %v", got, tt.want)
}
})
}
}
func TestUserAdminCanAssignRoles(t *testing.T) {
tests := []struct {
name string
roles []string
want bool
}{
{name: "client user", roles: []string{"client_user"}, want: true},
{name: "user admin", roles: []string{"user_admin"}, want: true},
{name: "client user and user admin", roles: []string{"client_user", "user_admin"}, want: true},
{name: "auditor rejected", roles: []string{"auditor"}, want: false},
{name: "super admin rejected", roles: []string{"super_admin"}, want: false},
{name: "unknown role rejected", roles: []string{"custom_role"}, want: false},
{name: "mixed allowed and protected rejected", roles: []string{"client_user", "auditor"}, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := userAdminCanAssignRoles(tt.roles); got != tt.want {
t.Fatalf("userAdminCanAssignRoles(%v) = %v, want %v", tt.roles, got, tt.want)
}
})
}
}
func TestCanAdminModeAssignRequestedRoles(t *testing.T) {
auditorRoles := []string{"auditor"}
clientRoles := []string{"client_user"}
tests := []struct {
name string
mode adminTeamMode
roles *[]string
want bool
}{
{name: "nil roles allowed", mode: adminTeamModeUserAdmin, roles: nil, want: true},
{name: "user admin can assign client role", mode: adminTeamModeUserAdmin, roles: &clientRoles, want: true},
{name: "user admin cannot assign auditor role", mode: adminTeamModeUserAdmin, roles: &auditorRoles, want: false},
{name: "super admin can assign auditor role", mode: adminTeamModeSuperAdmin, roles: &auditorRoles, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := canAdminModeAssignRequestedRoles(tt.mode, tt.roles); got != tt.want {
t.Fatalf("canAdminModeAssignRequestedRoles() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetAdminTeamMode(t *testing.T) {
server := newAdminListPermitServer(t)
defer server.Close()
ctrl := NewControllers(&Services{}, &mockAuthConfig{})
permitConfig := &usermanagement.PermitConfig{
APIKey: "test-api-key",
BaseURL: server.URL,
}
t.Run("disabled auth acts as super admin", func(t *testing.T) {
t.Setenv("DISABLE_AUTH", "true")
ctx, _ := newAdminAccessTestContext(http.MethodGet)
if got := ctrl.getAdminTeamMode(ctx, server.Client(), permitConfig); got != adminTeamModeSuperAdmin {
t.Fatalf("getAdminTeamMode() = %s, want %s", got, adminTeamModeSuperAdmin)
}
})
t.Run("permit subject roles determine mode", func(t *testing.T) {
t.Setenv("DISABLE_AUTH", "")
ctx, _ := newAdminAccessTestContext(http.MethodGet)
ctx.Set("user_claims", map[string]interface{}{"sub": "user-admin-sub"})
if got := ctrl.getAdminTeamMode(ctx, server.Client(), permitConfig); got != adminTeamModeUserAdmin {
t.Fatalf("getAdminTeamMode() = %s, want %s", got, adminTeamModeUserAdmin)
}
})
t.Run("context groups are used when subject is unavailable", func(t *testing.T) {
t.Setenv("DISABLE_AUTH", "")
ctx, _ := newAdminAccessTestContext(http.MethodGet)
ctx.Set("user_info", cognitoauth.UserInfo{Groups: []string{"auditor"}})
if got := ctrl.getAdminTeamMode(ctx, server.Client(), permitConfig); got != adminTeamModeAuditor {
t.Fatalf("getAdminTeamMode() = %s, want %s", got, adminTeamModeAuditor)
}
})
t.Run("no auth context has no admin mode", func(t *testing.T) {
t.Setenv("DISABLE_AUTH", "")
ctx, _ := newAdminAccessTestContext(http.MethodGet)
if got := ctrl.getAdminTeamMode(ctx, server.Client(), permitConfig); got != adminTeamModeNone {
t.Fatalf("getAdminTeamMode() = %s, want %s", got, adminTeamModeNone)
}
})
}
func TestGetAdminTargetAccess(t *testing.T) {
server := newAdminListPermitServer(t)
defer server.Close()
t.Setenv("DISABLE_AUTH", "false")
t.Setenv("PERMIT_IO_BASE_URL", server.URL)
ctrl := NewControllers(&Services{}, &mockAuthConfig{})
t.Run("loads target roles and caller mode", func(t *testing.T) {
ctx, _ := newAdminAccessTestContext(http.MethodGet)
ctx.Set("user_claims", map[string]interface{}{"sub": "user-admin-sub"})
cognitoUser := &usermanagement.CognitoUserResponse{
SubjectID: "client-sub",
Email: "client@example.com",
}
access := ctrl.getAdminTargetAccess(ctx, UserEmail(cognitoUser.Email), cognitoUser)
if access.mode != adminTeamModeUserAdmin {
t.Fatalf("getAdminTargetAccess().mode = %s, want %s", access.mode, adminTeamModeUserAdmin)
}
if !access.roleLookupOK {
t.Fatalf("getAdminTargetAccess().roleLookupOK = false, want true")
}
if len(access.roles) != 1 || access.roles[0] != "client_user" {
t.Fatalf("getAdminTargetAccess().roles = %v, want [client_user]", access.roles)
}
if !access.canRead(cognitoUser.Email) {
t.Fatalf("adminTargetAccess.canRead() = false, want true")
}
if !access.canMutate(cognitoUser.Email) {
t.Fatalf("adminTargetAccess.canMutate() = false, want true")
}
})
t.Run("user admin fail closes when target role lookup fails", func(t *testing.T) {
ctx, _ := newAdminAccessTestContext(http.MethodPatch)
ctx.Set("user_claims", map[string]interface{}{"sub": "user-admin-sub"})
cognitoUser := &usermanagement.CognitoUserResponse{
SubjectID: "role-error-sub",
Email: "client@example.com",
}
access := ctrl.getAdminTargetAccess(ctx, UserEmail(cognitoUser.Email), cognitoUser)
if access.mode != adminTeamModeUserAdmin {
t.Fatalf("getAdminTargetAccess().mode = %s, want %s", access.mode, adminTeamModeUserAdmin)
}
if access.roleLookupOK {
t.Fatalf("getAdminTargetAccess().roleLookupOK = true, want false")
}
if access.canRead(cognitoUser.Email) {
t.Fatalf("adminTargetAccess.canRead() = true, want false")
}
if access.canMutate(cognitoUser.Email) {
t.Fatalf("adminTargetAccess.canMutate() = true, want false")
}
})
}
func TestAdminUserErrorResponses(t *testing.T) {
ctx, rec := newAdminAccessTestContext(http.MethodPatch)
if err := adminUserForbidden(ctx); err != nil {
t.Fatalf("adminUserForbidden() error = %v", err)
}
if rec.Code != http.StatusForbidden {
t.Fatalf("adminUserForbidden() status = %d, want %d", rec.Code, http.StatusForbidden)
}
if !strings.Contains(rec.Body.String(), "Insufficient permissions") {
t.Fatalf("adminUserForbidden() body = %q, want permissions message", rec.Body.String())
}
ctx, rec = newAdminAccessTestContext(http.MethodGet)
if err := adminUserNotFound(ctx, "hidden@example.com"); err != nil {
t.Fatalf("adminUserNotFound() error = %v", err)
}
if rec.Code != http.StatusNotFound {
t.Fatalf("adminUserNotFound() status = %d, want %d", rec.Code, http.StatusNotFound)
}
if !strings.Contains(rec.Body.String(), "User not found: hidden@example.com") {
t.Fatalf("adminUserNotFound() body = %q, want not found message", rec.Body.String())
}
}
func TestAdminUserListPaginationAndPage(t *testing.T) {
pageInput := int32(2)
pageSizeInput := int32(2)
page, pageSize := adminUserListPagination(ListAdminUsersParams{
Page: &pageInput,
PageSize: &pageSizeInput,
})
if page != 2 || pageSize != 2 {
t.Fatalf("adminUserListPagination() = (%d, %d), want (2, 2)", page, pageSize)
}
tooLargePageSize := int32(100)
_, defaultedPageSize := adminUserListPagination(ListAdminUsersParams{PageSize: &tooLargePageSize})
if defaultedPageSize != 50 {
t.Fatalf("adminUserListPagination() pageSize = %d, want default 50", defaultedPageSize)
}
users := []AdminUserDetails{
{CognitoSubjectId: "sub-1"},
{CognitoSubjectId: "sub-2"},
{CognitoSubjectId: "sub-3"},
}
pageUsers, hasMore := paginateAdminUsers(users, 1, 2)
if len(pageUsers) != 2 || !hasMore {
t.Fatalf("paginateAdminUsers() = %d users, hasMore %v; want 2 users and hasMore true", len(pageUsers), hasMore)
}
pageUsers, hasMore = paginateAdminUsers(users, 2, 2)
if len(pageUsers) != 1 || hasMore {
t.Fatalf("paginateAdminUsers(page 2) = %d users, hasMore %v; want 1 user and hasMore false", len(pageUsers), hasMore)
}
}
func TestCollectAdminCognitoUsersStopsAtUserCap(t *testing.T) {
nextToken := "next"
_, err := collectAdminCognitoUsers(func(pageSize int, paginationToken *string) (*usermanagement.ListCognitoUsersResult, error) {
users := make([]usermanagement.CognitoUserResponse, pageSize+1)
for i := range users {
users[i] = usermanagement.CognitoUserResponse{SubjectID: "sub"}
}
return &usermanagement.ListCognitoUsersResult{Users: users, PaginationKey: &nextToken}, nil
})
if err == nil || !strings.Contains(err.Error(), "exceeded 6000 users") {
t.Fatalf("collectAdminCognitoUsers() error = %v, want user cap error", err)
}
}
func TestCollectAdminCognitoUsersStopsAtPageCap(t *testing.T) {
nextToken := "next"
_, err := collectAdminCognitoUsers(func(pageSize int, paginationToken *string) (*usermanagement.ListCognitoUsersResult, error) {
return &usermanagement.ListCognitoUsersResult{
Users: []usermanagement.CognitoUserResponse{{SubjectID: "sub"}},
PaginationKey: &nextToken,
}, nil
})
if err == nil || !strings.Contains(err.Error(), "exceeded 100 pages") {
t.Fatalf("collectAdminCognitoUsers() error = %v, want page cap error", err)
}
}
func TestVisibleAdminUsersAppliesDEVOPS722Visibility(t *testing.T) {
users := []usermanagement.CognitoUserResponse{
{SubjectID: "client-sub", Email: "client@example.com", FirstName: "Client", LastName: "User", Status: "CONFIRMED", Enabled: true},
{SubjectID: "user-admin-sub", Email: "admin@example.com", FirstName: "User", LastName: "Admin", Status: "CONFIRMED", Enabled: true},
{SubjectID: "aarete-sub", Email: "person@aarete.com", FirstName: "AArete", LastName: "User", Status: "CONFIRMED", Enabled: true},
{SubjectID: "auditor-sub", Email: "auditor@example.com", FirstName: "Audit", LastName: "User", Status: "CONFIRMED", Enabled: true},
{SubjectID: "super-sub", Email: "super@example.com", FirstName: "Super", LastName: "User", Status: "CONFIRMED", Enabled: true},
{SubjectID: "missing-role-sub", Email: "missing-role@example.com", FirstName: "Missing", LastName: "Role", Status: "CONFIRMED", Enabled: true},
}
rolesBySubject := map[string][]string{
"client-sub": {"client_user"},
"user-admin-sub": {"user_admin"},
"aarete-sub": {"client_user"},
"auditor-sub": {"auditor"},
"super-sub": {"super_admin"},
}
ctrl := NewControllers(&Services{}, &mockAuthConfig{})
userAdminVisible := ctrl.visibleAdminUsers(users, adminTeamModeUserAdmin, rolesBySubject)
assertVisibleSubjects(t, userAdminVisible, []string{"client-sub", "user-admin-sub"})
auditorVisible := ctrl.visibleAdminUsers(users, adminTeamModeAuditor, rolesBySubject)
assertVisibleSubjects(t, auditorVisible, []string{"client-sub", "user-admin-sub", "aarete-sub", "auditor-sub", "super-sub", "missing-role-sub"})
superAdminVisible := ctrl.visibleAdminUsers(users, adminTeamModeSuperAdmin, rolesBySubject)
assertVisibleSubjects(t, superAdminVisible, []string{"client-sub", "user-admin-sub", "aarete-sub", "auditor-sub", "super-sub", "missing-role-sub"})
}
func TestAdminRoleAssignmentsByUserGroupsRoles(t *testing.T) {
got := adminRoleAssignmentsByUser([]usermanagement.RoleAssignment{
{User: "sub-1", Role: "client_user", Tenant: "default"},
{User: "sub-1", Role: "user_admin", Tenant: "default"},
{User: "sub-2", Role: "auditor", Tenant: "default"},
{User: "", Role: "super_admin", Tenant: "default"},
{User: "sub-3", Role: "", Tenant: "default"},
})
assertVisibleRoleMap(t, got, map[string][]string{
"sub-1": {"client_user", "user_admin"},
"sub-2": {"auditor"},
})
}
func TestAdminUserDetailsRolesMarshalAsEmptyArray(t *testing.T) {
detail := adminUserDetailsFromCognito(usermanagement.CognitoUserResponse{
SubjectID: "sub-without-roles",
Email: "client@example.com",
}, nil)
body, err := json.Marshal(detail)
if err != nil {
t.Fatalf("marshal admin user details: %v", err)
}
if strings.Contains(string(body), `"roles":null`) {
t.Fatalf("admin user details marshaled roles as null: %s", body)
}
if !strings.Contains(string(body), `"roles":[]`) {
t.Fatalf("admin user details missing empty roles array: %s", body)
}
}
func TestUpdateUserRolesInPermitEmptyArrayClearsRoles(t *testing.T) {
var deletedRoles []string
mux := http.NewServeMux()
mux.HandleFunc("/v2/api-key/scope", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{
"organization_id": "test-org",
"project_id": "test-project",
"environment_id": "test-env",
}); err != nil {
t.Fatalf("encode scope response: %v", err)
}
})
mux.HandleFunc("/v2/facts/test-project/test-env/role_assignments", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
if err := json.NewEncoder(w).Encode([]map[string]string{
{"role": "client_user", "user": "target-sub", "tenant": "default"},
{"role": "user_admin", "user": "target-sub", "tenant": "default"},
}); err != nil {
t.Fatalf("encode role assignments: %v", err)
}
case http.MethodDelete:
var assignment usermanagement.RoleAssignment
if err := json.NewDecoder(r.Body).Decode(&assignment); err != nil {
t.Fatalf("decode delete role assignment: %v", err)
}
deletedRoles = append(deletedRoles, assignment.Role)
w.WriteHeader(http.StatusNoContent)
default:
t.Fatalf("unexpected method %s", r.Method)
}
})
roleServer := httptest.NewServer(mux)
defer roleServer.Close()
ctrl := NewControllers(&Services{}, &mockAuthConfig{})
ctx, _ := newAdminAccessTestContext(http.MethodPatch)
emptyRoles := []string{}
ctrl.updateUserRolesInPermit(
ctx,
UserEmail("target@example.com"),
AdminUserUpdate{Roles: &emptyRoles},
cognitoauth.UserInfo{Email: "admin@example.com"},
"target-sub",
roleServer.Client(),
&usermanagement.PermitConfig{APIKey: "test-api-key", BaseURL: roleServer.URL},
)
assertVisibleRoleMap(t, map[string][]string{"deleted": deletedRoles}, map[string][]string{
"deleted": {"client_user", "user_admin"},
})
}
func TestUpdateUserRolesInPermitAddsAndRemovesRoles(t *testing.T) {
var assignedRoles []string
var deletedRoles []string
mux := http.NewServeMux()
mux.HandleFunc("/v2/api-key/scope", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{
"organization_id": "test-org",
"project_id": "test-project",
"environment_id": "test-env",
}); err != nil {
t.Fatalf("encode scope response: %v", err)
}
})
mux.HandleFunc("/v2/facts/test-project/test-env/role_assignments", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
if err := json.NewEncoder(w).Encode([]map[string]string{
{"role": "client_user", "user": "target-sub", "tenant": "default"},
{"role": "user_admin", "user": "target-sub", "tenant": "default"},
}); err != nil {
t.Fatalf("encode role assignments: %v", err)
}
case http.MethodPost:
var assignment usermanagement.RoleAssignment
if err := json.NewDecoder(r.Body).Decode(&assignment); err != nil {
t.Fatalf("decode assign role assignment: %v", err)
}
assignedRoles = append(assignedRoles, assignment.Role)
w.WriteHeader(http.StatusCreated)
case http.MethodDelete:
var assignment usermanagement.RoleAssignment
if err := json.NewDecoder(r.Body).Decode(&assignment); err != nil {
t.Fatalf("decode delete role assignment: %v", err)
}
deletedRoles = append(deletedRoles, assignment.Role)
w.WriteHeader(http.StatusNoContent)
default:
t.Fatalf("unexpected method %s", r.Method)
}
})
roleServer := httptest.NewServer(mux)
defer roleServer.Close()
ctrl := NewControllers(&Services{}, &mockAuthConfig{})
ctx, _ := newAdminAccessTestContext(http.MethodPatch)
requestedRoles := []string{"client_user", "auditor"}
ctrl.updateUserRolesInPermit(
ctx,
UserEmail("target@example.com"),
AdminUserUpdate{Roles: &requestedRoles},
cognitoauth.UserInfo{Email: "admin@example.com"},
"target-sub",
roleServer.Client(),
&usermanagement.PermitConfig{APIKey: "test-api-key", BaseURL: roleServer.URL},
)
assertVisibleRoleMap(t, map[string][]string{
"assigned": assignedRoles,
"deleted": deletedRoles,
}, map[string][]string{
"assigned": {"auditor"},
"deleted": {"user_admin"},
})
}
func newAdminListPermitServer(t *testing.T) *httptest.Server {
t.Helper()
mux := http.NewServeMux()
mux.HandleFunc("/v2/api-key/scope", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{
"organization_id": "test-org",
"project_id": "test-project",
"environment_id": "test-env",
}); err != nil {
t.Fatalf("encode scope response: %v", err)
}
})
mux.HandleFunc("/v2/facts/test-project/test-env/role_assignments", func(w http.ResponseWriter, r *http.Request) {
user := r.URL.Query().Get("user")
if user == "role-error-sub" {
http.Error(w, "permit unavailable", http.StatusInternalServerError)
return
}
roleByUser := map[string]string{
"client-sub": "client_user",
"user-admin-sub": "user_admin",
"aarete-sub": "client_user",
"auditor-sub": "auditor",
"super-sub": "super_admin",
}
role := roleByUser[user]
assignments := []map[string]string{}
if role != "" {
assignments = append(assignments, map[string]string{"role": role, "user": user, "tenant": "default"})
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(assignments); err != nil {
t.Fatalf("encode role assignments: %v", err)
}
})
return httptest.NewServer(mux)
}
func newAdminAccessTestContext(method string) (echo.Context, *httptest.ResponseRecorder) {
e := echo.New()
req := httptest.NewRequest(method, "/admin/users", nil)
rec := httptest.NewRecorder()
return e.NewContext(req, rec), rec
}
func assertVisibleSubjects(t *testing.T, users []AdminUserDetails, want []string) {
t.Helper()
got := make([]string, 0, len(users))
for _, user := range users {
got = append(got, user.CognitoSubjectId)
}
if len(got) != len(want) {
t.Fatalf("visible subjects = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("visible subjects = %v, want %v", got, want)
}
}
}
func assertVisibleRoleMap(t *testing.T, got map[string][]string, want map[string][]string) {
t.Helper()
if len(got) != len(want) {
t.Fatalf("role map = %v, want %v", got, want)
}
for subject, wantRoles := range want {
gotRoles, ok := got[subject]
if !ok {
t.Fatalf("role map missing subject %q: %v", subject, got)
}
if len(gotRoles) != len(wantRoles) {
t.Fatalf("role map[%q] = %v, want %v", subject, gotRoles, wantRoles)
}
for i := range wantRoles {
if gotRoles[i] != wantRoles[i] {
t.Fatalf("role map[%q] = %v, want %v", subject, gotRoles, wantRoles)
}
}
}
}
+449 -443
View File
@@ -246,7 +246,7 @@ type AdminUserDetails struct {
// LastName User's family name retrieved from Cognito // LastName User's family name retrieved from Cognito
LastName *string `json:"last_name,omitempty"` LastName *string `json:"last_name,omitempty"`
// Roles Roles assigned in Permit.io. Only populated by GET /admin/users/{email}. Not populated by GET /admin/users (list endpoint) for performance reasons. // Roles Roles assigned in Permit.io. Populated by GET /admin/users and GET /admin/users/{email} so Team Management clients can enforce role-based visibility and action rules.
Roles *[]string `json:"roles,omitempty"` Roles *[]string `json:"roles,omitempty"`
// Status Current Cognito user account status // Status Current Cognito user account status
@@ -4147,448 +4147,454 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object // Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{ var swaggerSpec = []string{
"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",
"4r39fvdPAAAA//94J1oWMqcCAA==",
} }
// GetSwagger returns the content of the embedded swagger specification file // GetSwagger returns the content of the embedded swagger specification file
+17 -1
View File
@@ -1280,6 +1280,8 @@ Creates a new user in both AWS Cognito and Permit.io systems with optional role
**Security**: Requires JWT authentication **Security**: Requires JWT authentication
**Team Management Permissions**: Super Admin can create any user. User Admin and Client Admin can create only non-AArete users with `client_user` and/or `user_admin` roles. Auditor is read-only and cannot create users.
**Environment Scoping**: When `COGNITO_ENVIRONMENT_ID` is configured on the server, newly created users are automatically tagged with the `custom:environment_id` attribute matching the server's value. This ensures users are scoped to the correct environment. **Environment Scoping**: When `COGNITO_ENVIRONMENT_ID` is configured on the server, newly created users are automatically tagged with the `custom:environment_id` attribute matching the server's value. This ensures users are scoped to the correct environment.
**Creation Flow**: **Creation Flow**:
@@ -1341,11 +1343,13 @@ Lists users from AWS Cognito with pagination, filtering, and sorting support.
**Environment Filtering**: When `COGNITO_ENVIRONMENT_ID` is configured, the response is filtered to include only users whose `custom:environment_id` matches the server's value or users with no environment tag (legacy/untagged users). Users belonging to other environments are excluded from the results. **Environment Filtering**: When `COGNITO_ENVIRONMENT_ID` is configured, the response is filtered to include only users whose `custom:environment_id` matches the server's value or users with no environment tag (legacy/untagged users). Users belonging to other environments are excluded from the results.
**Team Management Visibility**: User Admin and Client Admin receive only non-AArete users that do not have `auditor` or `super_admin` roles. Super Admin and Auditor receive all environment-visible users. The `roles` field is populated from Permit.io for list responses.
**Query Parameters**: **Query Parameters**:
| Parameter | Type | Default | Description | | Parameter | Type | Default | Description |
|-----------|------|---------|-------------| |-----------|------|---------|-------------|
| page | integer | 1 | Page number (1-10000) | | page | integer | 1 | Page number (1-10000) |
| page_size | integer | 50 | Results per page (1-100) | | page_size | integer | 50 | Results per page (1-60) |
| search | string | - | Search term for email/name filtering | | search | string | - | Search term for email/name filtering |
| status | string | all | Filter by status: `enabled`, `disabled`, `all` | | status | string | all | Filter by status: `enabled`, `disabled`, `all` |
| sort_by | string | email | Sort field: `email`, `created_at`, `last_name` | | sort_by | string | email | Sort field: `email`, `created_at`, `last_name` |
@@ -1383,6 +1387,8 @@ Retrieves user details from both AWS Cognito and Permit.io by email address.
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment to prevent information leakage. Untagged users and users matching the server's environment are accessible. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment to prevent information leakage. Untagged users and users matching the server's environment are accessible.
**Team Management Visibility**: Hidden users return `404 Not Found` so User Admin and Client Admin callers cannot enumerate AArete, Auditor, or Super Admin users.
**Path Parameters**: **Path Parameters**:
- `email`: URL-encoded user email address (max 320 chars) - `email`: URL-encoded user email address (max 320 chars)
@@ -1397,6 +1403,8 @@ Checks if a user exists in Cognito and/or Permit.io without returning full detai
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404` for users belonging to a different environment. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404` for users belonging to a different environment.
**Team Management Visibility**: Hidden users return `404 Not Found` so User Admin and Client Admin callers cannot enumerate AArete, Auditor, or Super Admin users.
**Response Headers**: **Response Headers**:
- `X-User-Exists-Cognito`: boolean - `X-User-Exists-Cognito`: boolean
@@ -1415,6 +1423,8 @@ Updates user attributes (first_name, last_name) in Cognito and manages role assi
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment.
**Team Management Permissions**: Super Admin can update any environment-visible user. User Admin and Client Admin can update only non-AArete client-level users and can assign only `client_user` and `user_admin` roles. Auditor is read-only.
**Role Management (REPLACE Strategy)**: **Role Management (REPLACE Strategy)**:
- Providing a `roles` array REPLACES all existing roles (not additive) - Providing a `roles` array REPLACES all existing roles (not additive)
@@ -1443,6 +1453,8 @@ Permanently deletes a user from both AWS Cognito and Permit.io. This operation i
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment.
**Team Management Permissions**: Super Admin can delete any environment-visible user. User Admin and Client Admin can delete only non-AArete client-level users. Auditor is read-only.
**Query Parameters**: **Query Parameters**:
- `confirm` (required): Must be `true` to confirm deletion - `confirm` (required): Must be `true` to confirm deletion
@@ -1470,6 +1482,8 @@ Disables a user in AWS Cognito, preventing authentication. User data and roles a
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment.
**Team Management Permissions**: Super Admin can disable any environment-visible user. User Admin and Client Admin can disable only non-AArete client-level users. Auditor is read-only.
**Response** (`AdminUserActionResponse`): **Response** (`AdminUserActionResponse`):
```json ```json
@@ -1490,6 +1504,8 @@ Re-enables a previously disabled user in AWS Cognito. Restores authentication ca
**Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment. **Environment Ownership**: When `COGNITO_ENVIRONMENT_ID` is configured, returns `404 Not Found` for users belonging to a different environment.
**Team Management Permissions**: Super Admin can enable any environment-visible user. User Admin and Client Admin can enable only non-AArete client-level users. Auditor is read-only.
**Response**: Same as disable endpoint with `"action": "enable"` **Response**: Same as disable endpoint with `"action": "enable"`
--- ---
+10 -1
View File
@@ -268,6 +268,7 @@ This document provides a comprehensive summary of all REST API endpoints availab
- **POST /admin/users** - Create a new user - **POST /admin/users** - Create a new user
- Request Body: `{email, first_name, last_name, roles[]}` - Request Body: `{email, first_name, last_name, roles[]}`
- Creates user in both AWS Cognito and Permit.io - Creates user in both AWS Cognito and Permit.io
- Team Management: Super Admin can create any user; User Admin and Client Admin can create only non-AArete users with `client_user`/`user_admin` roles; Auditor is read-only
- Roles: Must exist in Permit.io (e.g., `super_admin`, `user_admin`, `auditor`, `client_user`) - Roles: Must exist in Permit.io (e.g., `super_admin`, `user_admin`, `auditor`, `client_user`)
- Returns: `{cognito_subject_id, email, first_name, last_name, status, enabled, permit_synced, roles_assigned[], created_at}` (201 Created or 200 OK if already exists) - Returns: `{cognito_subject_id, email, first_name, last_name, status, enabled, permit_synced, roles_assigned[], created_at}` (201 Created or 200 OK if already exists)
- **Idempotent**: Returns 200 OK if user already exists with matching attributes - **Idempotent**: Returns 200 OK if user already exists with matching attributes
@@ -275,18 +276,22 @@ This document provides a comprehensive summary of all REST API endpoints availab
- **GET /admin/users** - List users with pagination, filtering, and sorting - **GET /admin/users** - List users with pagination, filtering, and sorting
- Query Parameters: - Query Parameters:
- `page` (optional, default: 1, max: 10000) - Page number - `page` (optional, default: 1, max: 10000) - Page number
- `page_size` (optional, default: 50, max: 100) - Results per page - `page_size` (optional, default: 50, max: 60) - Results per page
- `search` (optional) - Search term for email/name filtering - `search` (optional) - Search term for email/name filtering
- `status` (optional, default: `all`) - Filter by status: `enabled`, `disabled`, `all` - `status` (optional, default: `all`) - Filter by status: `enabled`, `disabled`, `all`
- `sort_by` (optional, default: `email`) - Sort field: `email`, `created_at`, `last_name` - `sort_by` (optional, default: `email`) - Sort field: `email`, `created_at`, `last_name`
- `sort_order` (optional, default: `asc`) - Sort direction: `asc`, `desc` - `sort_order` (optional, default: `asc`) - Sort direction: `asc`, `desc`
- Team Management: User Admin and Client Admin see only non-AArete users without `auditor`/`super_admin` roles; Super Admin and Auditor see all environment-visible users
- `roles` is populated from Permit.io for each returned user
- Returns: `{users[], total, page, page_size, has_more}` - Returns: `{users[], total, page, page_size, has_more}`
#### Individual User Operations #### Individual User Operations
- **GET /admin/users/{email}** - Get user by email - **GET /admin/users/{email}** - Get user by email
- Hidden users return 404 for User Admin and Client Admin callers
- Returns: `{cognito_subject_id, email, first_name, last_name, status, enabled, roles[], created_at, updated_at}` - Returns: `{cognito_subject_id, email, first_name, last_name, status, enabled, roles[], created_at, updated_at}`
- **HEAD /admin/users/{email}** - Check if user exists - **HEAD /admin/users/{email}** - Check if user exists
- Hidden users return 404 for User Admin and Client Admin callers
- Returns: 200 OK with headers: - Returns: 200 OK with headers:
- `X-User-Exists-Cognito: boolean` - Whether user exists in Cognito - `X-User-Exists-Cognito: boolean` - Whether user exists in Cognito
- `X-User-Exists-PermitIO: boolean` - Whether user exists in Permit.io - `X-User-Exists-PermitIO: boolean` - Whether user exists in Permit.io
@@ -294,6 +299,7 @@ This document provides a comprehensive summary of all REST API endpoints availab
- **PATCH /admin/users/{email}** - Update user attributes and roles - **PATCH /admin/users/{email}** - Update user attributes and roles
- Request Body: `{first_name?, last_name?, roles[]?}` - Request Body: `{first_name?, last_name?, roles[]?}`
- Team Management: Super Admin can update any environment-visible user; User Admin and Client Admin can update only non-AArete client-level users and assign only `client_user`/`user_admin`; Auditor is read-only
- **Role Management**: Providing `roles[]` REPLACES all existing roles (not additive) - **Role Management**: Providing `roles[]` REPLACES all existing roles (not additive)
- To add a role: Include all current roles PLUS the new role - To add a role: Include all current roles PLUS the new role
- To remove a role: Include only roles you want to keep - To remove a role: Include only roles you want to keep
@@ -304,6 +310,7 @@ This document provides a comprehensive summary of all REST API endpoints availab
- **DELETE /admin/users/{email}** - Delete user permanently - **DELETE /admin/users/{email}** - Delete user permanently
- Query Parameters: - Query Parameters:
- `confirm=true` (required) - Must be set to confirm deletion - `confirm=true` (required) - Must be set to confirm deletion
- Team Management: Super Admin can delete any environment-visible user; User Admin and Client Admin can delete only non-AArete client-level users; Auditor is read-only
- Deletes from both AWS Cognito and Permit.io - Deletes from both AWS Cognito and Permit.io
- **Irreversible operation** - **Irreversible operation**
- Returns: `{success, email, cognito_subject_id, deleted_from{cognito, permit}, timestamp}` (200 OK or 207 Multi-Status for partial deletion) - Returns: `{success, email, cognito_subject_id, deleted_from{cognito, permit}, timestamp}` (200 OK or 207 Multi-Status for partial deletion)
@@ -311,12 +318,14 @@ This document provides a comprehensive summary of all REST API endpoints availab
#### User Account Status Management #### User Account Status Management
- **POST /admin/users/{email}/disable** - Disable user - **POST /admin/users/{email}/disable** - Disable user
- Disables user in AWS Cognito, preventing authentication - Disables user in AWS Cognito, preventing authentication
- Team Management: Super Admin can disable any environment-visible user; User Admin and Client Admin can disable only non-AArete client-level users; Auditor is read-only
- User data and roles are preserved - User data and roles are preserved
- **Idempotent**: Safe to call multiple times - **Idempotent**: Safe to call multiple times
- Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK) - Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK)
- **POST /admin/users/{email}/enable** - Enable user - **POST /admin/users/{email}/enable** - Enable user
- Re-enables a previously disabled user in AWS Cognito - Re-enables a previously disabled user in AWS Cognito
- Team Management: Super Admin can enable any environment-visible user; User Admin and Client Admin can enable only non-AArete client-level users; Auditor is read-only
- Restores authentication capability - Restores authentication capability
- **Idempotent**: Safe to call multiple times - **Idempotent**: Safe to call multiple times
- Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK) - Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK)
+89 -28
View File
@@ -13,6 +13,11 @@ import (
"strings" "strings"
) )
const (
permitRoleAssignmentsPerPage = 100
maxPermitRoleAssignmentPages = 100
)
// CreatePermitUser creates a new user in Permit.io using data from a Cognito user. // CreatePermitUser creates a new user in Permit.io using data from a Cognito user.
// The Cognito Subject ID is used as the Permit.io user key for consistency across systems. // The Cognito Subject ID is used as the Permit.io user key for consistency across systems.
// //
@@ -504,42 +509,98 @@ func GetPermitUserRoles(client *http.Client, config *PermitConfig, userKey strin
return nil, fmt.Errorf("error reading response: %w", err) return nil, fmt.Errorf("error reading response: %w", err)
} }
roles := []string{} // Initialize as empty slice, not nil assignments, err := decodePermitRoleAssignments(body)
var assignments []interface{} if err != nil {
return nil, err
// First try to unmarshal as direct array (common case)
var directAssignments []interface{}
if err := json.Unmarshal(body, &directAssignments); err == nil {
assignments = directAssignments
} else {
// If that fails, try as object with data/items/etc
var response map[string]interface{}
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("error parsing response as object or array: %w", err)
}
// Look for role assignments in common response structures
if data, ok := response["data"].([]interface{}); ok {
assignments = data
} else if items, ok := response["items"].([]interface{}); ok {
assignments = items
} else if results, ok := response["results"].([]interface{}); ok {
assignments = results
}
} }
// Extract role names from assignments roles := make([]string, 0, len(assignments))
for _, assignment := range assignments { for _, assignment := range assignments {
if roleAssignment, ok := assignment.(map[string]interface{}); ok { if assignment.Role != "" {
if role, ok := roleAssignment["role"].(string); ok { roles = append(roles, assignment.Role)
roles = append(roles, role)
}
} }
} }
return roles, nil return roles, nil
} }
// ListAllPermitRoleAssignments retrieves all role assignments in the configured Permit.io environment.
func ListAllPermitRoleAssignments(client *http.Client, config *PermitConfig) ([]RoleAssignment, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
assignments := []RoleAssignment{}
for page := 1; page <= maxPermitRoleAssignmentPages; page++ {
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/role_assignments?page=%d&per_page=%d",
config.BaseURL, projectID, envID, page, permitRoleAssignmentsPerPage)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
body, readErr := io.ReadAll(resp.Body)
closeErr := resp.Body.Close()
if readErr != nil {
return nil, fmt.Errorf("error reading response: %w", readErr)
}
if closeErr != nil {
return nil, fmt.Errorf("error closing response body: %w", closeErr)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
pageAssignments, err := decodePermitRoleAssignments(body)
if err != nil {
return nil, err
}
assignments = append(assignments, pageAssignments...)
if len(pageAssignments) < permitRoleAssignmentsPerPage {
return assignments, nil
}
}
return nil, fmt.Errorf("Permit.io role assignment listing exceeded %d pages", maxPermitRoleAssignmentPages)
}
func decodePermitRoleAssignments(body []byte) ([]RoleAssignment, error) {
var directAssignments []RoleAssignment
if err := json.Unmarshal(body, &directAssignments); err == nil {
return directAssignments, nil
}
var response struct {
Data []RoleAssignment `json:"data"`
Items []RoleAssignment `json:"items"`
Results []RoleAssignment `json:"results"`
}
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("error parsing response as object or array: %w", err)
}
switch {
case response.Data != nil:
return response.Data, nil
case response.Items != nil:
return response.Items, nil
case response.Results != nil:
return response.Results, nil
default:
return nil, fmt.Errorf("unexpected Permit.io role assignments response format")
}
}
// GetExistingRoles retrieves all role definitions from the specified Permit.io project/environment. // GetExistingRoles retrieves all role definitions from the specified Permit.io project/environment.
// //
// Parameters: // Parameters:
+76
View File
@@ -89,6 +89,82 @@ func TestGetPermitUserRolesEscapesUserKey(t *testing.T) {
assert.Equal(t, []string{"super_admin"}, roles) assert.Equal(t, []string{"super_admin"}, roles)
} }
func TestListAllPermitRoleAssignmentsDirectArray(t *testing.T) {
requests := 0
server, config := createMockPermitServer(t, map[string]http.HandlerFunc{
"/v2/facts/test-project/test-env/role_assignments": func(w http.ResponseWriter, r *http.Request) {
requests++
assert.Empty(t, r.URL.Query().Get("user"))
assert.Equal(t, "1", r.URL.Query().Get("page"))
assert.Equal(t, "100", r.URL.Query().Get("per_page"))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`[{"role":"client_user","user":"sub-1","tenant":"default"},{"role":"user_admin","user":"sub-1","tenant":"default"}]`))
require.NoError(t, err)
},
})
defer server.Close()
assignments, err := ListAllPermitRoleAssignments(server.Client(), config)
require.NoError(t, err)
require.Len(t, assignments, 2)
assert.Equal(t, "client_user", assignments[0].Role)
assert.Equal(t, "sub-1", assignments[0].User)
assert.Equal(t, 1, requests)
}
func TestListAllPermitRoleAssignmentsObjectWrappedPagination(t *testing.T) {
requests := 0
server, config := createMockPermitServer(t, map[string]http.HandlerFunc{
"/v2/facts/test-project/test-env/role_assignments": func(w http.ResponseWriter, r *http.Request) {
requests++
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if r.URL.Query().Get("page") == "1" {
page := make([]map[string]string, 0, 100)
for i := 0; i < 100; i++ {
page = append(page, map[string]string{"role": "client_user", "user": "sub-page-1", "tenant": "default"})
}
err := json.NewEncoder(w).Encode(map[string]interface{}{"data": page})
require.NoError(t, err)
return
}
err := json.NewEncoder(w).Encode(map[string]interface{}{
"data": []map[string]string{{"role": "auditor", "user": "sub-page-2", "tenant": "default"}},
})
require.NoError(t, err)
},
})
defer server.Close()
assignments, err := ListAllPermitRoleAssignments(server.Client(), config)
require.NoError(t, err)
assert.Len(t, assignments, 101)
assert.Equal(t, "auditor", assignments[100].Role)
assert.Equal(t, "sub-page-2", assignments[100].User)
assert.Equal(t, 2, requests)
}
func TestListAllPermitRoleAssignmentsRejectsUnknownSuccessfulShape(t *testing.T) {
server, config := createMockPermitServer(t, map[string]http.HandlerFunc{
"/v2/facts/test-project/test-env/role_assignments": func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`{"unexpected":[]}`))
require.NoError(t, err)
},
})
defer server.Close()
assignments, err := ListAllPermitRoleAssignments(server.Client(), config)
require.Error(t, err)
assert.Nil(t, assignments)
assert.Contains(t, err.Error(), "unexpected Permit.io role assignments response format")
}
// TestGetAPIKeyScope_OrgLevelKey tests API key with only organization scope // TestGetAPIKeyScope_OrgLevelKey tests API key with only organization scope
func TestGetAPIKeyScope_OrgLevelKey(t *testing.T) { func TestGetAPIKeyScope_OrgLevelKey(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+1 -1
View File
@@ -244,7 +244,7 @@ type AdminUserDetails struct {
// LastName User's family name retrieved from Cognito // LastName User's family name retrieved from Cognito
LastName *string `json:"last_name,omitempty"` LastName *string `json:"last_name,omitempty"`
// Roles Roles assigned in Permit.io. Only populated by GET /admin/users/{email}. Not populated by GET /admin/users (list endpoint) for performance reasons. // Roles Roles assigned in Permit.io. Populated by GET /admin/users and GET /admin/users/{email} so Team Management clients can enforce role-based visibility and action rules.
Roles *[]string `json:"roles,omitempty"` Roles *[]string `json:"roles,omitempty"`
// Status Current Cognito user account status // Status Current Cognito user account status
+30 -7
View File
@@ -1659,6 +1659,9 @@ paths:
description: >- description: >-
Creates a new user in both AWS Cognito and Permit.io systems with optional role assignments. Creates a new user in both AWS Cognito and Permit.io systems with optional role assignments.
Idempotent - returns 200 OK if user already exists with identical attributes. Idempotent - returns 200 OK if user already exists with identical attributes.
Team Management permissions are enforced before user creation: Super Admin can
create any user, User Admin and Client Admin can create only non-AArete users
with client_user/user_admin roles, and Auditor cannot create users.
**Creation Flow:** **Creation Flow:**
@@ -1745,9 +1748,12 @@ paths:
summary: List users summary: List users
description: >- description: >-
Lists users from AWS Cognito with pagination, filtering, and sorting support. Lists users from AWS Cognito with pagination, filtering, and sorting support.
Note: The roles field is not populated in this response. To retrieve roles for Results are filtered according to the viewer's Team Management role. User
a specific user, call GET /admin/users/{email} which returns the full user Admin and Client Admin viewers only receive non-AArete users that do not have
details including Permit.io roles. auditor or super_admin roles, while Super Admin and Auditor viewers receive all
environment-visible users.
The roles field is populated from Permit.io so clients can enforce the same
visibility and action rules locally.
security: security:
- jwtAuth: [] - jwtAuth: []
parameters: parameters:
@@ -1767,7 +1773,7 @@ paths:
type: integer type: integer
format: int32 format: int32
minimum: 1 minimum: 1
maximum: 100 maximum: 60
default: 50 default: 50
- name: search - name: search
in: query in: query
@@ -1828,6 +1834,8 @@ paths:
summary: Get user by email summary: Get user by email
description: >- description: >-
Retrieves user details from both AWS Cognito and Permit.io by email address. Retrieves user details from both AWS Cognito and Permit.io by email address.
Team Management visibility is enforced. Hidden users return 404 so User Admin
and Client Admin callers cannot enumerate AArete, Auditor, or Super Admin users.
security: security:
- jwtAuth: [] - jwtAuth: []
responses: responses:
@@ -1859,6 +1867,8 @@ paths:
summary: Check if user exists summary: Check if user exists
description: >- description: >-
Checks if a user exists in Cognito and/or Permit.io without returning full details. Checks if a user exists in Cognito and/or Permit.io without returning full details.
Team Management visibility is enforced. Hidden users return 404 so User Admin
and Client Admin callers cannot enumerate AArete, Auditor, or Super Admin users.
security: security:
- jwtAuth: [] - jwtAuth: []
responses: responses:
@@ -1898,6 +1908,10 @@ paths:
description: >- description: >-
Updates user attributes (first_name, last_name) in Cognito and manages role assignments in Permit.io. Updates user attributes (first_name, last_name) in Cognito and manages role assignments in Permit.io.
Email address cannot be changed (use email path parameter to identify user). Email address cannot be changed (use email path parameter to identify user).
Team Management mutation rules are enforced: Super Admin can update any
environment-visible user, User Admin and Client Admin can update only non-AArete
client-level users and can assign only client_user/user_admin roles, and Auditor
is read-only.
**Attribute Updates:** **Attribute Updates:**
@@ -1986,6 +2000,9 @@ paths:
description: >- description: >-
Permanently deletes a user from both AWS Cognito and Permit.io. Permanently deletes a user from both AWS Cognito and Permit.io.
This operation is irreversible. Requires confirm=true query parameter. This operation is irreversible. Requires confirm=true query parameter.
Team Management mutation rules are enforced: Super Admin can delete any
environment-visible user, User Admin and Client Admin can delete only non-AArete
client-level users, and Auditor is read-only.
security: security:
- jwtAuth: [] - jwtAuth: []
parameters: parameters:
@@ -2038,6 +2055,9 @@ paths:
description: >- description: >-
Disables a user in AWS Cognito, preventing authentication. Disables a user in AWS Cognito, preventing authentication.
User data and roles are preserved. Idempotent. User data and roles are preserved. Idempotent.
Team Management mutation rules are enforced: Super Admin can disable any
environment-visible user, User Admin and Client Admin can disable only non-AArete
client-level users, and Auditor is read-only.
security: security:
- jwtAuth: [] - jwtAuth: []
responses: responses:
@@ -2074,6 +2094,9 @@ paths:
description: >- description: >-
Re-enables a previously disabled user in AWS Cognito. Re-enables a previously disabled user in AWS Cognito.
Restores authentication capability. Idempotent. Restores authentication capability. Idempotent.
Team Management mutation rules are enforced: Super Admin can enable any
environment-visible user, User Admin and Client Admin can enable only non-AArete
client-level users, and Auditor is read-only.
security: security:
- jwtAuth: [] - jwtAuth: []
responses: responses:
@@ -5326,8 +5349,9 @@ components:
maxLength: 100 maxLength: 100
pattern: "^.+$" pattern: "^.+$"
description: >- description: >-
Roles assigned in Permit.io. Only populated by GET /admin/users/{email}. Roles assigned in Permit.io. Populated by GET /admin/users and
Not populated by GET /admin/users (list endpoint) for performance reasons. GET /admin/users/{email} so Team Management clients can enforce
role-based visibility and action rules.
example: ["admin", "viewer"] example: ["admin", "viewer"]
created_at: created_at:
type: string type: string
@@ -5362,7 +5386,6 @@ components:
example: Doe-Smith example: Doe-Smith
roles: roles:
type: array type: array
minItems: 1
maxItems: 50 maxItems: 50
items: items:
type: string type: string