Browse Source
test: add IAM group management integration tests
test: add IAM group management integration tests
Add comprehensive integration tests for group CRUD, membership, policy attachment, policy enforcement, disabled group behavior, user deletion side effects, and multi-group membership. Add "group" test type to CI matrix in s3-iam-tests workflow.pull/8560/head
3 changed files with 705 additions and 2 deletions
@ -0,0 +1,693 @@ |
|||
package iam |
|||
|
|||
import ( |
|||
"encoding/xml" |
|||
"io" |
|||
"net/http" |
|||
"net/url" |
|||
"strings" |
|||
"testing" |
|||
"time" |
|||
|
|||
"github.com/aws/aws-sdk-go/aws" |
|||
"github.com/aws/aws-sdk-go/aws/credentials" |
|||
"github.com/aws/aws-sdk-go/aws/session" |
|||
"github.com/aws/aws-sdk-go/service/iam" |
|||
"github.com/aws/aws-sdk-go/service/s3" |
|||
"github.com/stretchr/testify/assert" |
|||
"github.com/stretchr/testify/require" |
|||
) |
|||
|
|||
// TestIAMGroupLifecycle tests the full lifecycle of group management:
|
|||
// CreateGroup, GetGroup, ListGroups, DeleteGroup
|
|||
func TestIAMGroupLifecycle(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-group-lifecycle" |
|||
|
|||
t.Run("create_group", func(t *testing.T) { |
|||
resp, err := iamClient.CreateGroup(&iam.CreateGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
assert.Equal(t, groupName, *resp.Group.GroupName) |
|||
}) |
|||
|
|||
t.Run("get_group", func(t *testing.T) { |
|||
resp, err := iamClient.GetGroup(&iam.GetGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
assert.Equal(t, groupName, *resp.Group.GroupName) |
|||
}) |
|||
|
|||
t.Run("list_groups_contains_created", func(t *testing.T) { |
|||
resp, err := iamClient.ListGroups(&iam.ListGroupsInput{}) |
|||
require.NoError(t, err) |
|||
found := false |
|||
for _, g := range resp.Groups { |
|||
if *g.GroupName == groupName { |
|||
found = true |
|||
break |
|||
} |
|||
} |
|||
assert.True(t, found, "Created group should appear in ListGroups") |
|||
}) |
|||
|
|||
t.Run("create_duplicate_group_fails", func(t *testing.T) { |
|||
_, err := iamClient.CreateGroup(&iam.CreateGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
assert.Error(t, err, "Creating a duplicate group should fail") |
|||
}) |
|||
|
|||
t.Run("delete_group", func(t *testing.T) { |
|||
_, err := iamClient.DeleteGroup(&iam.DeleteGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify it's gone
|
|||
resp, err := iamClient.ListGroups(&iam.ListGroupsInput{}) |
|||
require.NoError(t, err) |
|||
for _, g := range resp.Groups { |
|||
assert.NotEqual(t, groupName, *g.GroupName, |
|||
"Deleted group should not appear in ListGroups") |
|||
} |
|||
}) |
|||
|
|||
t.Run("delete_nonexistent_group_fails", func(t *testing.T) { |
|||
_, err := iamClient.DeleteGroup(&iam.DeleteGroupInput{ |
|||
GroupName: aws.String("nonexistent-group-xyz"), |
|||
}) |
|||
assert.Error(t, err) |
|||
}) |
|||
} |
|||
|
|||
// TestIAMGroupMembership tests adding and removing users from groups
|
|||
func TestIAMGroupMembership(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-group-members" |
|||
userName := "test-user-for-group" |
|||
|
|||
// Setup: create group and user
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(groupName)}) |
|||
|
|||
_, err = iamClient.CreateUser(&iam.CreateUserInput{ |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(userName)}) |
|||
|
|||
t.Run("add_user_to_group", func(t *testing.T) { |
|||
_, err := iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
}) |
|||
|
|||
t.Run("get_group_shows_member", func(t *testing.T) { |
|||
resp, err := iamClient.GetGroup(&iam.GetGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
found := false |
|||
for _, u := range resp.Users { |
|||
if *u.UserName == userName { |
|||
found = true |
|||
break |
|||
} |
|||
} |
|||
assert.True(t, found, "Added user should appear in GetGroup members") |
|||
}) |
|||
|
|||
t.Run("list_groups_for_user", func(t *testing.T) { |
|||
resp, err := iamClient.ListGroupsForUser(&iam.ListGroupsForUserInput{ |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
found := false |
|||
for _, g := range resp.Groups { |
|||
if *g.GroupName == groupName { |
|||
found = true |
|||
break |
|||
} |
|||
} |
|||
assert.True(t, found, "Group should appear in ListGroupsForUser") |
|||
}) |
|||
|
|||
t.Run("add_duplicate_member_is_idempotent", func(t *testing.T) { |
|||
_, err := iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
// Should succeed (idempotent) or return a benign error
|
|||
// AWS IAM allows duplicate add without error
|
|||
assert.NoError(t, err) |
|||
}) |
|||
|
|||
t.Run("remove_user_from_group", func(t *testing.T) { |
|||
_, err := iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify removal
|
|||
resp, err := iamClient.GetGroup(&iam.GetGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
for _, u := range resp.Users { |
|||
assert.NotEqual(t, userName, *u.UserName, |
|||
"Removed user should not appear in group members") |
|||
} |
|||
}) |
|||
} |
|||
|
|||
// TestIAMGroupPolicyAttachment tests attaching and detaching policies from groups
|
|||
func TestIAMGroupPolicyAttachment(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-group-policies" |
|||
policyName := "test-group-attach-policy" |
|||
policyDoc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:ListBucket","Resource":"*"}]}` |
|||
|
|||
// Setup: create group and policy
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(groupName)}) |
|||
|
|||
createPolicyResp, err := iamClient.CreatePolicy(&iam.CreatePolicyInput{ |
|||
PolicyName: aws.String(policyName), |
|||
PolicyDocument: aws.String(policyDoc), |
|||
}) |
|||
require.NoError(t, err) |
|||
policyArn := createPolicyResp.Policy.Arn |
|||
defer iamClient.DeletePolicy(&iam.DeletePolicyInput{PolicyArn: policyArn}) |
|||
|
|||
t.Run("attach_group_policy", func(t *testing.T) { |
|||
_, err := iamClient.AttachGroupPolicy(&iam.AttachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), |
|||
PolicyArn: policyArn, |
|||
}) |
|||
require.NoError(t, err) |
|||
}) |
|||
|
|||
t.Run("list_attached_group_policies", func(t *testing.T) { |
|||
resp, err := iamClient.ListAttachedGroupPolicies(&iam.ListAttachedGroupPoliciesInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
found := false |
|||
for _, p := range resp.AttachedPolicies { |
|||
if *p.PolicyName == policyName { |
|||
found = true |
|||
break |
|||
} |
|||
} |
|||
assert.True(t, found, "Attached policy should appear in ListAttachedGroupPolicies") |
|||
}) |
|||
|
|||
t.Run("detach_group_policy", func(t *testing.T) { |
|||
_, err := iamClient.DetachGroupPolicy(&iam.DetachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), |
|||
PolicyArn: policyArn, |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify detachment
|
|||
resp, err := iamClient.ListAttachedGroupPolicies(&iam.ListAttachedGroupPoliciesInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
for _, p := range resp.AttachedPolicies { |
|||
assert.NotEqual(t, policyName, *p.PolicyName, |
|||
"Detached policy should not appear in ListAttachedGroupPolicies") |
|||
} |
|||
}) |
|||
} |
|||
|
|||
// TestIAMGroupPolicyEnforcement tests that group policies are enforced during S3 operations.
|
|||
// Creates a user with no direct policies, adds them to a group with S3 access,
|
|||
// and verifies they can access S3 through the group policy.
|
|||
func TestIAMGroupPolicyEnforcement(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-enforcement-group" |
|||
userName := "test-enforcement-user" |
|||
policyName := "test-enforcement-policy" |
|||
bucketName := "test-group-enforce-bucket" |
|||
policyDoc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::` + bucketName + `","arn:aws:s3:::` + bucketName + `/*"]}]}` |
|||
|
|||
// Create user
|
|||
_, err = iamClient.CreateUser(&iam.CreateUserInput{ |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer func() { |
|||
iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(userName)}) |
|||
}() |
|||
|
|||
// Create access key for the user
|
|||
keyResp, err := iamClient.CreateAccessKey(&iam.CreateAccessKeyInput{ |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteAccessKey(&iam.DeleteAccessKeyInput{ |
|||
UserName: aws.String(userName), |
|||
AccessKeyId: keyResp.AccessKey.AccessKeyId, |
|||
}) |
|||
|
|||
accessKeyId := *keyResp.AccessKey.AccessKeyId |
|||
secretKey := *keyResp.AccessKey.SecretAccessKey |
|||
|
|||
// Create an S3 client with the user's credentials
|
|||
userS3Client := createS3Client(t, accessKeyId, secretKey) |
|||
|
|||
// Create group
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer func() { |
|||
iamClient.DetachGroupPolicy(&iam.DetachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), |
|||
PolicyArn: aws.String("arn:aws:iam:::policy/" + policyName), |
|||
}) |
|||
iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(groupName)}) |
|||
}() |
|||
|
|||
// Create policy
|
|||
createPolicyResp, err := iamClient.CreatePolicy(&iam.CreatePolicyInput{ |
|||
PolicyName: aws.String(policyName), |
|||
PolicyDocument: aws.String(policyDoc), |
|||
}) |
|||
require.NoError(t, err) |
|||
policyArn := createPolicyResp.Policy.Arn |
|||
defer iamClient.DeletePolicy(&iam.DeletePolicyInput{PolicyArn: policyArn}) |
|||
|
|||
t.Run("user_without_group_denied", func(t *testing.T) { |
|||
// User has no policies and is not in any group — should be denied
|
|||
_, err := userS3Client.CreateBucket(&s3.CreateBucketInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
assert.Error(t, err, "User without any policies should be denied") |
|||
}) |
|||
|
|||
t.Run("user_with_group_policy_allowed", func(t *testing.T) { |
|||
// Attach policy to group
|
|||
_, err := iamClient.AttachGroupPolicy(&iam.AttachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), |
|||
PolicyArn: policyArn, |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Add user to group
|
|||
_, err = iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Wait for policy propagation
|
|||
time.Sleep(2 * time.Second) |
|||
|
|||
// Now user should be able to create the bucket through group policy
|
|||
_, err = userS3Client.CreateBucket(&s3.CreateBucketInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
require.NoError(t, err, "User with group policy should be allowed") |
|||
defer func() { |
|||
userS3Client.DeleteBucket(&s3.DeleteBucketInput{Bucket: aws.String(bucketName)}) |
|||
}() |
|||
|
|||
// Should also be able to put/get objects
|
|||
_, err = userS3Client.PutObject(&s3.PutObjectInput{ |
|||
Bucket: aws.String(bucketName), |
|||
Key: aws.String("test-key"), |
|||
Body: aws.ReadSeekCloser(strings.NewReader("test-data")), |
|||
}) |
|||
require.NoError(t, err, "User should be able to put objects through group policy") |
|||
}) |
|||
|
|||
t.Run("user_removed_from_group_denied", func(t *testing.T) { |
|||
// Remove user from group
|
|||
_, err := iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Wait for policy propagation
|
|||
time.Sleep(2 * time.Second) |
|||
|
|||
// User should now be denied
|
|||
_, err = userS3Client.ListObjects(&s3.ListObjectsInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
assert.Error(t, err, "User removed from group should be denied") |
|||
}) |
|||
} |
|||
|
|||
// TestIAMGroupDisabledPolicyEnforcement tests that disabled groups do not contribute policies.
|
|||
// Uses the raw IAM API (callIAMAPI) since the AWS SDK doesn't support custom group status.
|
|||
func TestIAMGroupDisabledPolicyEnforcement(t *testing.T) { |
|||
if testing.Short() { |
|||
t.Skip("Skipping integration test in short mode") |
|||
} |
|||
if !isSeaweedFSRunning(t) { |
|||
t.Skip("SeaweedFS is not running at", TestIAMEndpoint) |
|||
} |
|||
|
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-disabled-group" |
|||
userName := "test-disabled-grp-user" |
|||
policyName := "test-disabled-grp-policy" |
|||
bucketName := "test-disabled-grp-bucket" |
|||
policyDoc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::` + bucketName + `","arn:aws:s3:::` + bucketName + `/*"]}]}` |
|||
|
|||
// Create user, group, policy
|
|||
_, err = iamClient.CreateUser(&iam.CreateUserInput{UserName: aws.String(userName)}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(userName)}) |
|||
|
|||
keyResp, err := iamClient.CreateAccessKey(&iam.CreateAccessKeyInput{UserName: aws.String(userName)}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteAccessKey(&iam.DeleteAccessKeyInput{ |
|||
UserName: aws.String(userName), AccessKeyId: keyResp.AccessKey.AccessKeyId, |
|||
}) |
|||
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{GroupName: aws.String(groupName)}) |
|||
require.NoError(t, err) |
|||
defer func() { |
|||
iamClient.DetachGroupPolicy(&iam.DetachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), |
|||
PolicyArn: aws.String("arn:aws:iam:::policy/" + policyName), |
|||
}) |
|||
iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(groupName), UserName: aws.String(userName), |
|||
}) |
|||
iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(groupName)}) |
|||
}() |
|||
|
|||
createPolicyResp, err := iamClient.CreatePolicy(&iam.CreatePolicyInput{ |
|||
PolicyName: aws.String(policyName), PolicyDocument: aws.String(policyDoc), |
|||
}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeletePolicy(&iam.DeletePolicyInput{PolicyArn: createPolicyResp.Policy.Arn}) |
|||
|
|||
// Setup: attach policy, add user, create bucket with admin
|
|||
_, err = iamClient.AttachGroupPolicy(&iam.AttachGroupPolicyInput{ |
|||
GroupName: aws.String(groupName), PolicyArn: createPolicyResp.Policy.Arn, |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
_, err = iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(groupName), UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
time.Sleep(2 * time.Second) |
|||
|
|||
userS3Client := createS3Client(t, *keyResp.AccessKey.AccessKeyId, *keyResp.AccessKey.SecretAccessKey) |
|||
|
|||
// Create bucket using admin first so we can test listing
|
|||
adminS3, err := framework.CreateS3ClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
_, err = adminS3.CreateBucket(&s3.CreateBucketInput{Bucket: aws.String(bucketName)}) |
|||
require.NoError(t, err) |
|||
defer adminS3.DeleteBucket(&s3.DeleteBucketInput{Bucket: aws.String(bucketName)}) |
|||
|
|||
t.Run("enabled_group_allows_access", func(t *testing.T) { |
|||
_, err := userS3Client.ListObjects(&s3.ListObjectsInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
assert.NoError(t, err, "User in enabled group should have access") |
|||
}) |
|||
|
|||
t.Run("disabled_group_denies_access", func(t *testing.T) { |
|||
// Disable group via raw IAM API (no SDK support for this extension)
|
|||
resp, err := callIAMAPI(t, "UpdateGroup", url.Values{ |
|||
"GroupName": {groupName}, |
|||
"Disabled": {"true"}, |
|||
}) |
|||
require.NoError(t, err) |
|||
defer resp.Body.Close() |
|||
|
|||
// Wait for propagation
|
|||
time.Sleep(2 * time.Second) |
|||
|
|||
_, err = userS3Client.ListObjects(&s3.ListObjectsInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
assert.Error(t, err, "User in disabled group should be denied access") |
|||
}) |
|||
|
|||
t.Run("re_enabled_group_restores_access", func(t *testing.T) { |
|||
// Re-enable the group
|
|||
resp, err := callIAMAPI(t, "UpdateGroup", url.Values{ |
|||
"GroupName": {groupName}, |
|||
"Disabled": {"false"}, |
|||
}) |
|||
require.NoError(t, err) |
|||
defer resp.Body.Close() |
|||
|
|||
// Wait for propagation
|
|||
time.Sleep(2 * time.Second) |
|||
|
|||
_, err = userS3Client.ListObjects(&s3.ListObjectsInput{ |
|||
Bucket: aws.String(bucketName), |
|||
}) |
|||
assert.NoError(t, err, "User in re-enabled group should have access again") |
|||
}) |
|||
} |
|||
|
|||
// TestIAMGroupUserDeletionSideEffect tests that deleting a user removes them from all groups.
|
|||
func TestIAMGroupUserDeletionSideEffect(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
groupName := "test-deletion-group" |
|||
userName := "test-deletion-user" |
|||
|
|||
// Create group and user
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{GroupName: aws.String(groupName)}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(groupName)}) |
|||
|
|||
_, err = iamClient.CreateUser(&iam.CreateUserInput{UserName: aws.String(userName)}) |
|||
require.NoError(t, err) |
|||
|
|||
// Add user to group
|
|||
_, err = iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(groupName), |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify user is in group
|
|||
getResp, err := iamClient.GetGroup(&iam.GetGroupInput{GroupName: aws.String(groupName)}) |
|||
require.NoError(t, err) |
|||
assert.Len(t, getResp.Users, 1, "Group should have 1 member before deletion") |
|||
|
|||
// Delete the user
|
|||
_, err = iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(userName)}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify user was removed from the group
|
|||
getResp, err = iamClient.GetGroup(&iam.GetGroupInput{GroupName: aws.String(groupName)}) |
|||
require.NoError(t, err) |
|||
assert.Empty(t, getResp.Users, "Group should have no members after user deletion") |
|||
} |
|||
|
|||
// TestIAMGroupMultipleGroups tests that a user can belong to multiple groups
|
|||
// and inherits policies from all of them.
|
|||
func TestIAMGroupMultipleGroups(t *testing.T) { |
|||
framework := NewS3IAMTestFramework(t) |
|||
defer framework.Cleanup() |
|||
|
|||
iamClient, err := framework.CreateIAMClientWithJWT("admin-user", "TestAdminRole") |
|||
require.NoError(t, err) |
|||
|
|||
group1 := "test-multi-group-1" |
|||
group2 := "test-multi-group-2" |
|||
userName := "test-multi-group-user" |
|||
|
|||
// Create two groups
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{GroupName: aws.String(group1)}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(group1)}) |
|||
|
|||
_, err = iamClient.CreateGroup(&iam.CreateGroupInput{GroupName: aws.String(group2)}) |
|||
require.NoError(t, err) |
|||
defer iamClient.DeleteGroup(&iam.DeleteGroupInput{GroupName: aws.String(group2)}) |
|||
|
|||
// Create user
|
|||
_, err = iamClient.CreateUser(&iam.CreateUserInput{UserName: aws.String(userName)}) |
|||
require.NoError(t, err) |
|||
defer func() { |
|||
iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(group1), UserName: aws.String(userName), |
|||
}) |
|||
iamClient.RemoveUserFromGroup(&iam.RemoveUserFromGroupInput{ |
|||
GroupName: aws.String(group2), UserName: aws.String(userName), |
|||
}) |
|||
iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(userName)}) |
|||
}() |
|||
|
|||
// Add user to both groups
|
|||
_, err = iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(group1), UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
_, err = iamClient.AddUserToGroup(&iam.AddUserToGroupInput{ |
|||
GroupName: aws.String(group2), UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
|
|||
// Verify user appears in both groups
|
|||
resp, err := iamClient.ListGroupsForUser(&iam.ListGroupsForUserInput{ |
|||
UserName: aws.String(userName), |
|||
}) |
|||
require.NoError(t, err) |
|||
groupNames := make(map[string]bool) |
|||
for _, g := range resp.Groups { |
|||
groupNames[*g.GroupName] = true |
|||
} |
|||
assert.True(t, groupNames[group1], "User should be in group 1") |
|||
assert.True(t, groupNames[group2], "User should be in group 2") |
|||
} |
|||
|
|||
// --- Response types for raw IAM API calls ---
|
|||
|
|||
type CreateGroupResponse struct { |
|||
XMLName xml.Name `xml:"CreateGroupResponse"` |
|||
CreateGroupResult struct { |
|||
Group struct { |
|||
GroupName string `xml:"GroupName"` |
|||
} `xml:"Group"` |
|||
} `xml:"CreateGroupResult"` |
|||
} |
|||
|
|||
type ListGroupsResponse struct { |
|||
XMLName xml.Name `xml:"ListGroupsResponse"` |
|||
ListGroupsResult struct { |
|||
Groups []struct { |
|||
GroupName string `xml:"GroupName"` |
|||
} `xml:"Groups>member"` |
|||
} `xml:"ListGroupsResult"` |
|||
} |
|||
|
|||
// TestIAMGroupRawAPI tests group operations using raw HTTP IAM API calls,
|
|||
// for operations not covered by the AWS SDK (like the SeaweedFS extension
|
|||
// to disable/enable groups via UpdateGroup with Disabled parameter).
|
|||
func TestIAMGroupRawAPI(t *testing.T) { |
|||
if testing.Short() { |
|||
t.Skip("Skipping integration test in short mode") |
|||
} |
|||
if !isSeaweedFSRunning(t) { |
|||
t.Skip("SeaweedFS is not running at", TestIAMEndpoint) |
|||
} |
|||
|
|||
groupName := "test-raw-api-group" |
|||
|
|||
t.Run("create_group_raw", func(t *testing.T) { |
|||
resp, err := callIAMAPI(t, "CreateGroup", url.Values{ |
|||
"GroupName": {groupName}, |
|||
}) |
|||
require.NoError(t, err) |
|||
defer resp.Body.Close() |
|||
assert.Equal(t, http.StatusOK, resp.StatusCode) |
|||
|
|||
body, err := io.ReadAll(resp.Body) |
|||
require.NoError(t, err) |
|||
|
|||
var createResp CreateGroupResponse |
|||
err = xml.Unmarshal(body, &createResp) |
|||
require.NoError(t, err) |
|||
assert.Equal(t, groupName, createResp.CreateGroupResult.Group.GroupName) |
|||
}) |
|||
|
|||
t.Run("list_groups_raw", func(t *testing.T) { |
|||
resp, err := callIAMAPI(t, "ListGroups", url.Values{}) |
|||
require.NoError(t, err) |
|||
defer resp.Body.Close() |
|||
assert.Equal(t, http.StatusOK, resp.StatusCode) |
|||
|
|||
body, err := io.ReadAll(resp.Body) |
|||
require.NoError(t, err) |
|||
|
|||
var listResp ListGroupsResponse |
|||
err = xml.Unmarshal(body, &listResp) |
|||
require.NoError(t, err) |
|||
|
|||
found := false |
|||
for _, g := range listResp.ListGroupsResult.Groups { |
|||
if g.GroupName == groupName { |
|||
found = true |
|||
break |
|||
} |
|||
} |
|||
assert.True(t, found, "Created group should appear in raw ListGroups") |
|||
}) |
|||
|
|||
t.Run("delete_group_raw", func(t *testing.T) { |
|||
resp, err := callIAMAPI(t, "DeleteGroup", url.Values{ |
|||
"GroupName": {groupName}, |
|||
}) |
|||
require.NoError(t, err) |
|||
defer resp.Body.Close() |
|||
assert.Equal(t, http.StatusOK, resp.StatusCode) |
|||
}) |
|||
} |
|||
|
|||
// createS3Client creates an S3 client with static credentials
|
|||
func createS3Client(t *testing.T, accessKey, secretKey string) *s3.S3 { |
|||
sess, err := session.NewSession(&aws.Config{ |
|||
Region: aws.String("us-east-1"), |
|||
Endpoint: aws.String(TestS3Endpoint), |
|||
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""), |
|||
DisableSSL: aws.Bool(true), |
|||
S3ForcePathStyle: aws.Bool(true), |
|||
}) |
|||
require.NoError(t, err) |
|||
return s3.New(sess) |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue