Files
Jacob Mathison e3d9047143 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
2026-05-12 16:49:12 +00:00

196 lines
5.0 KiB
Go

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