You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
7.6 KiB
259 lines
7.6 KiB
package s3api
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/providers"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestSelectPrimaryRole(t *testing.T) {
|
|
s3iam := &S3IAMIntegration{}
|
|
|
|
t.Run("single_role_returns_that_role", func(t *testing.T) {
|
|
roles := []string{"admin"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "admin", result)
|
|
})
|
|
|
|
t.Run("explicit_primary_role_takes_precedence", func(t *testing.T) {
|
|
roles := []string{"admin", "reader", "writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: map[string]string{
|
|
"primary_role": "reader",
|
|
},
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "reader", result)
|
|
})
|
|
|
|
t.Run("explicit_primary_role_case_insensitive", func(t *testing.T) {
|
|
roles := []string{"Admin", "Reader", "Writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: map[string]string{
|
|
"primary_role": "admin",
|
|
},
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "Admin", result)
|
|
})
|
|
|
|
t.Run("invalid_primary_role_falls_back_to_hierarchy", func(t *testing.T) {
|
|
roles := []string{"admin", "reader", "writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: map[string]string{
|
|
"primary_role": "nonexistent",
|
|
},
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "admin", result) // Should select admin via hierarchy
|
|
})
|
|
|
|
t.Run("hierarchy_selection_admin_over_reader", func(t *testing.T) {
|
|
roles := []string{"reader", "admin", "writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "admin", result) // Admin has higher priority
|
|
})
|
|
|
|
t.Run("hierarchy_selection_case_insensitive", func(t *testing.T) {
|
|
roles := []string{"Reader", "ADMIN", "writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "ADMIN", result)
|
|
})
|
|
|
|
t.Run("hierarchy_selection_contains_match", func(t *testing.T) {
|
|
roles := []string{"system-reader", "system-admin-user", "system-writer"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "system-admin-user", result) // Contains "admin"
|
|
})
|
|
|
|
t.Run("deterministic_fallback_alphabetical", func(t *testing.T) {
|
|
// Roles that don't match any hierarchy
|
|
roles := []string{"zebra", "alpha", "beta"}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "alpha", result) // First alphabetically
|
|
})
|
|
|
|
t.Run("complex_enterprise_roles", func(t *testing.T) {
|
|
roles := []string{
|
|
"app-user-readonly",
|
|
"app-user-contributor",
|
|
"app-admin-full",
|
|
"system-guest",
|
|
}
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(roles, externalIdentity)
|
|
assert.Equal(t, "app-admin-full", result) // Contains "admin"
|
|
})
|
|
}
|
|
|
|
func TestSelectByRoleHierarchy(t *testing.T) {
|
|
s3iam := &S3IAMIntegration{}
|
|
|
|
t.Run("super_admin_highest_priority", func(t *testing.T) {
|
|
roles := []string{"admin", "super-admin", "reader"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "super-admin", result)
|
|
})
|
|
|
|
t.Run("admin_over_manager", func(t *testing.T) {
|
|
roles := []string{"manager", "admin", "reader"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "admin", result)
|
|
})
|
|
|
|
t.Run("manager_over_editor", func(t *testing.T) {
|
|
roles := []string{"editor", "manager", "reader"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "manager", result)
|
|
})
|
|
|
|
t.Run("editor_over_viewer", func(t *testing.T) {
|
|
roles := []string{"viewer", "editor"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "editor", result)
|
|
})
|
|
|
|
t.Run("no_hierarchy_match_returns_empty", func(t *testing.T) {
|
|
roles := []string{"custom-role-1", "custom-role-2", "special-user"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "", result)
|
|
})
|
|
|
|
t.Run("multiple_same_tier_returns_first_found", func(t *testing.T) {
|
|
roles := []string{"viewer", "reader", "guest"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
// Should return first match found in the hierarchy (viewer comes first in tier definition)
|
|
assert.Equal(t, "viewer", result)
|
|
})
|
|
|
|
t.Run("case_variations", func(t *testing.T) {
|
|
roles := []string{"ADMIN", "Reader", "writer"}
|
|
result := s3iam.selectByRoleHierarchy(roles)
|
|
assert.Equal(t, "ADMIN", result)
|
|
})
|
|
}
|
|
|
|
func TestRoleSelectionIntegration(t *testing.T) {
|
|
t.Run("real_world_enterprise_scenario", func(t *testing.T) {
|
|
// Simulate a real enterprise OIDC token with multiple roles
|
|
testCases := []struct {
|
|
name string
|
|
roles []string
|
|
primaryRole string // explicit primary_role claim
|
|
expectedRole string
|
|
selectionType string
|
|
}{
|
|
{
|
|
name: "explicit_primary_overrides_hierarchy",
|
|
roles: []string{"admin", "reader", "writer"},
|
|
primaryRole: "reader",
|
|
expectedRole: "reader",
|
|
selectionType: "explicit",
|
|
},
|
|
{
|
|
name: "hierarchy_selects_admin_over_others",
|
|
roles: []string{"contributor", "admin", "viewer"},
|
|
primaryRole: "", // No explicit primary
|
|
expectedRole: "admin",
|
|
selectionType: "hierarchy",
|
|
},
|
|
{
|
|
name: "deterministic_fallback_for_unknown_roles",
|
|
roles: []string{"zebra-role", "alpha-role", "beta-role"},
|
|
primaryRole: "",
|
|
expectedRole: "alpha-role",
|
|
selectionType: "deterministic",
|
|
},
|
|
{
|
|
name: "complex_enterprise_naming",
|
|
roles: []string{"org-user-readonly", "org-power-user", "org-system-admin"},
|
|
primaryRole: "",
|
|
expectedRole: "org-system-admin", // Contains "admin"
|
|
selectionType: "hierarchy",
|
|
},
|
|
}
|
|
|
|
s3iam := &S3IAMIntegration{}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
externalIdentity := &providers.ExternalIdentity{
|
|
Attributes: make(map[string]string),
|
|
}
|
|
if tc.primaryRole != "" {
|
|
externalIdentity.Attributes["primary_role"] = tc.primaryRole
|
|
}
|
|
|
|
result := s3iam.selectPrimaryRole(tc.roles, externalIdentity)
|
|
assert.Equal(t, tc.expectedRole, result,
|
|
"Expected %s selection to return %s, got %s",
|
|
tc.selectionType, tc.expectedRole, result)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test helper function to verify role parsing improvements
|
|
func TestRoleParsingImprovements(t *testing.T) {
|
|
t.Run("whitespace_handling", func(t *testing.T) {
|
|
// Test the improved role parsing logic
|
|
rolesStr := " admin , reader , writer "
|
|
roles := strings.Split(rolesStr, ",")
|
|
|
|
// Clean up role names (this is what the main code does now)
|
|
var cleanRoles []string
|
|
for _, role := range roles {
|
|
cleanRole := strings.TrimSpace(role)
|
|
if cleanRole != "" {
|
|
cleanRoles = append(cleanRoles, cleanRole)
|
|
}
|
|
}
|
|
|
|
expected := []string{"admin", "reader", "writer"}
|
|
assert.Equal(t, expected, cleanRoles)
|
|
})
|
|
|
|
t.Run("empty_roles_filtered", func(t *testing.T) {
|
|
rolesStr := "admin,,reader, ,writer"
|
|
roles := strings.Split(rolesStr, ",")
|
|
|
|
var cleanRoles []string
|
|
for _, role := range roles {
|
|
cleanRole := strings.TrimSpace(role)
|
|
if cleanRole != "" {
|
|
cleanRoles = append(cleanRoles, cleanRole)
|
|
}
|
|
}
|
|
|
|
expected := []string{"admin", "reader", "writer"}
|
|
assert.Equal(t, expected, cleanRoles)
|
|
})
|
|
}
|