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, }) }