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
+167 -46
View File
@@ -18,6 +18,12 @@ import (
"github.com/oapi-codegen/runtime/types"
)
const (
adminCognitoListPageSize = 60
maxAdminCognitoListPages = 100
maxAdminCognitoListUsers = 6000
)
// 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.
// 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
awsCfg, err := aws.GetAWSConfig(context.Background())
if err != nil {
@@ -204,9 +216,6 @@ func (ctrl *Controllers) CreateAdminUser(ctx echo.Context) error {
)
// Create user in Permit.io
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
permitSynced := false
err = usermanagement.CreatePermitUser(httpClient, permitConfig, cognitoUser)
if err != nil {
@@ -354,14 +363,11 @@ func (ctrl *Controllers) GetAdminUser(ctx echo.Context, email UserEmail) error {
})
}
// Get user roles from Permit.io
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
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)
targetAccess := ctrl.getAdminTargetAccess(ctx, email, cognitoUser)
if !targetAccess.canRead(cognitoUser.Email) {
return adminUserNotFound(ctx, string(email))
}
roles := targetAccess.roles
// Log successful retrieval
usermanagement.LogUserListAction(
@@ -422,6 +428,11 @@ func (ctrl *Controllers) CheckAdminUserExists(ctx echo.Context, email UserEmail)
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)
}
@@ -453,42 +464,33 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
Region: ctrl.cfg.GetAuthRegion(),
}
// Set default pagination parameters
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
}
page, pageSize := adminUserListPagination(params)
// List users from Cognito (note: Cognito doesn't support traditional page numbers, only pagination tokens)
// For simplicity, we'll just return the first page
result, err := usermanagement.ListCognitoUsers(context.Background(), cognitoClient, cognitoConfig.UserPoolID, int(pageSize), nil)
allUsers, err := listAllAdminCognitoUsers(cognitoClient, cognitoConfig.UserPoolID)
if err != nil {
ctrl.cfg.GetAuthLogger().Error("Failed to list users from Cognito", "error", err)
return ctx.JSON(http.StatusBadGateway, ErrorMessage{
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
filteredUsers := usermanagement.FilterUsersByEnvironmentID(result.Users, ctrl.getCognitoEnvironmentID())
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
adminMode := ctrl.getAdminTeamMode(ctx, httpClient, permitConfig)
if adminMode == adminTeamModeNone {
return adminUserForbidden(ctx)
}
// Convert to AdminUserDetails format
users := make([]AdminUserDetails, 0, len(filteredUsers))
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
usermanagement.LogUserListAction(
@@ -500,9 +502,7 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
)
// Build response
hasMore := result.PaginationKey != nil && *result.PaginationKey != ""
// 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
total := int32(len(visibleUsers)) // #nosec G115 -- admin user lists are bounded by Cognito user pool size
response := AdminUserList{
Users: users,
Total: total,
@@ -514,6 +514,106 @@ func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersP
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.
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 {
@@ -576,7 +676,7 @@ func (ctrl *Controllers) updateUserAttributesInCognito(ctx echo.Context, cognito
// 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) {
if req.Roles == nil || len(*req.Roles) == 0 {
if req.Roles == nil {
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
err = ctrl.updateUserAttributesInCognito(ctx, cognitoClient, cognitoConfig, email, req, adminUser, cognitoUser)
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
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
ctrl.updateUserRolesInPermit(ctx, email, req, adminUser, cognitoUser.SubjectID, httpClient, permitConfig)
// 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
cognitoDeleted := false
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
httpClient := &http.Client{Timeout: 10 * time.Second}
permitConfig := ctrl.getPermitConfig()
permitDeleted := false
err = usermanagement.DeletePermitUser(httpClient, permitConfig, cognitoUser.SubjectID)
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
err = usermanagement.DisableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email))
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
err = usermanagement.EnableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email))
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 *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"`
// Status Current Cognito user account status
@@ -4147,448 +4147,454 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"4r39fvdPAAAA//94J1oWMqcCAA==",
}
// 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
**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.
**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.
**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**:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| 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 |
| status | string | all | Filter by status: `enabled`, `disabled`, `all` |
| 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.
**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**:
- `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.
**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**:
- `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.
**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)**:
- 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.
**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**:
- `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.
**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`):
```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.
**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"`
---
+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
- Request Body: `{email, first_name, last_name, roles[]}`
- 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`)
- 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
@@ -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
- Query Parameters:
- `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
- `status` (optional, default: `all`) - Filter by status: `enabled`, `disabled`, `all`
- `sort_by` (optional, default: `email`) - Sort field: `email`, `created_at`, `last_name`
- `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}`
#### Individual User Operations
- **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}`
- **HEAD /admin/users/{email}** - Check if user exists
- Hidden users return 404 for User Admin and Client Admin callers
- Returns: 200 OK with headers:
- `X-User-Exists-Cognito: boolean` - Whether user exists in Cognito
- `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
- 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)
- To add a role: Include all current roles PLUS the new role
- 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
- Query Parameters:
- `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
- **Irreversible operation**
- 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
- **POST /admin/users/{email}/disable** - Disable user
- 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
- **Idempotent**: Safe to call multiple times
- Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK)
- **POST /admin/users/{email}/enable** - Enable user
- 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
- **Idempotent**: Safe to call multiple times
- Returns: `{success, email, action, cognito_subject_id, timestamp}` (200 OK)
+89 -28
View File
@@ -13,6 +13,11 @@ import (
"strings"
)
const (
permitRoleAssignmentsPerPage = 100
maxPermitRoleAssignmentPages = 100
)
// 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.
//
@@ -504,42 +509,98 @@ func GetPermitUserRoles(client *http.Client, config *PermitConfig, userKey strin
return nil, fmt.Errorf("error reading response: %w", err)
}
roles := []string{} // Initialize as empty slice, not nil
var assignments []interface{}
assignments, err := decodePermitRoleAssignments(body)
if err != nil {
return nil, err
}
// First try to unmarshal as direct array (common case)
var directAssignments []interface{}
roles := make([]string, 0, len(assignments))
for _, assignment := range assignments {
if assignment.Role != "" {
roles = append(roles, assignment.Role)
}
}
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 {
assignments = directAssignments
} else {
// If that fails, try as object with data/items/etc
var response map[string]interface{}
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)
}
// 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
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")
}
}
// Extract role names from assignments
for _, assignment := range assignments {
if roleAssignment, ok := assignment.(map[string]interface{}); ok {
if role, ok := roleAssignment["role"].(string); ok {
roles = append(roles, role)
}
}
}
return roles, nil
}
// GetExistingRoles retrieves all role definitions from the specified Permit.io project/environment.
//
// Parameters:
+76
View File
@@ -89,6 +89,82 @@ func TestGetPermitUserRolesEscapesUserKey(t *testing.T) {
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
func TestGetAPIKeyScope_OrgLevelKey(t *testing.T) {
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 *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"`
// Status Current Cognito user account status
+30 -7
View File
@@ -1659,6 +1659,9 @@ paths:
description: >-
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.
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:**
@@ -1745,9 +1748,12 @@ paths:
summary: List users
description: >-
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
a specific user, call GET /admin/users/{email} which returns the full user
details including Permit.io roles.
Results are filtered according to the viewer's Team Management role. User
Admin and Client Admin viewers only receive non-AArete users that do not have
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:
- jwtAuth: []
parameters:
@@ -1767,7 +1773,7 @@ paths:
type: integer
format: int32
minimum: 1
maximum: 100
maximum: 60
default: 50
- name: search
in: query
@@ -1828,6 +1834,8 @@ paths:
summary: Get user by email
description: >-
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:
- jwtAuth: []
responses:
@@ -1859,6 +1867,8 @@ paths:
summary: Check if user exists
description: >-
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:
- jwtAuth: []
responses:
@@ -1898,6 +1908,10 @@ paths:
description: >-
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).
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:**
@@ -1986,6 +2000,9 @@ paths:
description: >-
Permanently deletes a user from both AWS Cognito and Permit.io.
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:
- jwtAuth: []
parameters:
@@ -2038,6 +2055,9 @@ paths:
description: >-
Disables a user in AWS Cognito, preventing authentication.
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:
- jwtAuth: []
responses:
@@ -2074,6 +2094,9 @@ paths:
description: >-
Re-enables a previously disabled user in AWS Cognito.
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:
- jwtAuth: []
responses:
@@ -5326,8 +5349,9 @@ components:
maxLength: 100
pattern: "^.+$"
description: >-
Roles assigned in Permit.io. Only populated by GET /admin/users/{email}.
Not populated by GET /admin/users (list endpoint) for performance reasons.
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.
example: ["admin", "viewer"]
created_at:
type: string
@@ -5362,7 +5386,6 @@ components:
example: Doe-Smith
roles:
type: array
minItems: 1
maxItems: 50
items:
type: string