e3d9047143
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
196 lines
5.0 KiB
Go
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,
|
|
})
|
|
}
|