diff --git a/cmd/versitygw/admin.go b/cmd/versitygw/admin.go index ef22465..efda268 100644 --- a/cmd/versitygw/admin.go +++ b/cmd/versitygw/admin.go @@ -289,11 +289,14 @@ func listUsers(ctx *cli.Context) error { } defer resp.Body.Close() + if resp.StatusCode >= 400 { + return fmt.Errorf("%s", body) + } + var accs []auth.Account if err := json.Unmarshal(body, &accs); err != nil { return err } - fmt.Println(accs) printAcctTable(accs) @@ -402,7 +405,7 @@ func listBuckets(ctx *cli.Context) error { defer resp.Body.Close() if resp.StatusCode >= 400 { - return fmt.Errorf(string(body)) + return fmt.Errorf("%s", body) } var buckets []s3response.Bucket diff --git a/cmd/versitygw/test.go b/cmd/versitygw/test.go index ac88cb3..0c5db4c 100644 --- a/cmd/versitygw/test.go +++ b/cmd/versitygw/test.go @@ -79,6 +79,11 @@ func initTestCommands() []*cli.Command { Usage: "Tests posix specific features", Action: getAction(integration.TestPosix), }, + { + Name: "iam", + Usage: "Tests iam service", + Action: getAction(integration.TestIAM), + }, { Name: "bench", Usage: "Runs download/upload performance test on the gateway", diff --git a/integration/group-tests.go b/integration/group-tests.go index 7d6400f..c09fecb 100644 --- a/integration/group-tests.go +++ b/integration/group-tests.go @@ -279,6 +279,13 @@ func TestPosix(s *S3Conf) { CreateMultipartUpload_dir_obj(s) } +func TestIAM(s *S3Conf) { + IAM_user_access_denied(s) + IAM_userplus_access_denied(s) + IAM_userplus_CreateBucket(s) + IAM_admin_ChangeBucketOwner(s) +} + type IntTests map[string]func(s *S3Conf) error func GetIntTests() IntTests { @@ -440,5 +447,9 @@ func GetIntTests() IntTests { "PutObject_overwrite_file_obj": PutObject_overwrite_file_obj, "PutObject_dir_obj_with_data": PutObject_dir_obj_with_data, "CreateMultipartUpload_dir_obj": CreateMultipartUpload_dir_obj, + "IAM_user_access_denied": IAM_user_access_denied, + "IAM_userplus_access_denied": IAM_userplus_access_denied, + "IAM_userplus_CreateBucket": IAM_userplus_CreateBucket, + "IAM_admin_ChangeBucketOwner": IAM_admin_ChangeBucketOwner, } } diff --git a/integration/tests.go b/integration/tests.go index ee2d498..d8fb45b 100644 --- a/integration/tests.go +++ b/integration/tests.go @@ -5177,6 +5177,162 @@ func GetBucketAcl_success(s *S3Conf) error { }) } +// IAM related tests +// multi-user iam tests +func IAM_user_access_denied(s *S3Conf) error { + testName := "IAM_user_access_denied" + runF(testName) + + usr := user{ + access: "grt1", + secret: "grt1secret", + role: "user", + } + err := deleteUser(s, usr.access) + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + + err = createUsers(s, []user{usr}) + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + + out, err := execCommand("admin", "-a", usr.access, "-s", usr.secret, "-er", s.endpoint, "delete-user", "-a", "random_access") + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + if !strings.Contains(string(out), adminAccessDeniedMsg) { + failF("%v: expected response error message to be %v, instead got %s", testName, adminAccessDeniedMsg, out) + return fmt.Errorf("%v: expected response error message to be %v, instead got %s", testName, adminAccessDeniedMsg, out) + } + + passF(testName) + + return nil +} + +func IAM_userplus_access_denied(s *S3Conf) error { + testName := "IAM_userplus_access_denied" + runF(testName) + + usr := user{ + access: "grt1", + secret: "grt1secret", + role: "userplus", + } + err := deleteUser(s, usr.access) + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + + err = createUsers(s, []user{usr}) + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + + out, err := execCommand("admin", "-a", usr.access, "-s", usr.secret, "-er", s.endpoint, "delete-user", "-a", "random_access") + if err != nil { + failF("%v: %v", testName, err) + return fmt.Errorf("%v: %w", testName, err) + } + if !strings.Contains(string(out), adminAccessDeniedMsg) { + failF("%v: expected response error message to be %v, instead got %s", testName, adminAccessDeniedMsg, out) + return fmt.Errorf("%v: expected response error message to be %v, instead got %s", testName, adminAccessDeniedMsg, out) + } + + passF(testName) + + return nil +} + +func IAM_userplus_CreateBucket(s *S3Conf) error { + testName := "IAM_userplus_CreateBucket" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + usr := user{ + access: "grt1", + secret: "grt1secret", + role: "userplus", + } + err := deleteUser(s, usr.access) + if err != nil { + return err + } + + err = createUsers(s, []user{usr}) + if err != nil { + return err + } + + cfg := *s + cfg.awsID = usr.access + cfg.awsSecret = usr.secret + + bckt := getBucketName() + err = setup(&cfg, bckt) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.HeadBucket(ctx, &s3.HeadBucketInput{Bucket: &bckt}) + cancel() + if err != nil { + return err + } + + err = teardown(&cfg, bckt) + if err != nil { + return err + } + + return nil + }) +} + +func IAM_admin_ChangeBucketOwner(s *S3Conf) error { + testName := "IAM_admin_ChangeBucketOwner" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + admin := user{ + access: "admin1", + secret: "admin1secret", + role: "admin", + } + usr := user{ + access: "grt1", + secret: "grt1secret", + role: "user", + } + err := createUsers(s, []user{admin, usr}) + if err != nil { + return err + } + + err = changeBucketsOwner(s, []string{bucket}, usr.access) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + resp, err := s3client.GetBucketAcl(ctx, &s3.GetBucketAclInput{Bucket: &bucket}) + cancel() + if err != nil { + return err + } + + if *resp.Owner.ID != usr.access { + return fmt.Errorf("expected the bucket owner to be %v, instead got %v", usr.access, *resp.Owner.ID) + } + + return nil + }) +} + // Posix related tests func PutObject_overwrite_dir_obj(s *S3Conf) error { testName := "PutObject_overwrite_dir_obj" diff --git a/integration/utils.go b/integration/utils.go index 1a69127..563ae28 100644 --- a/integration/utils.go +++ b/integration/utils.go @@ -28,9 +28,11 @@ import ( ) var ( - bcktCount = 0 - succUsrCrt = "The user has been created successfully" - failUsrCrt = "failed to create a user: update iam data: account already exists" + bcktCount = 0 + succUsrCrt = "The user has been created successfully" + failUsrCrt = "failed to create a user: update iam data: account already exists" + adminAccessDeniedMsg = "access denied: only admin users have access to this resource" + succDeleteUserMsg = "The user has been deleted successfully" ) func getBucketName() string { @@ -541,6 +543,18 @@ func createUsers(s *S3Conf, users []user) error { return nil } +func deleteUser(s *S3Conf, access string) error { + out, err := execCommand("admin", "-a", s.awsID, "-s", s.awsSecret, "-er", s.endpoint, "delete-user", "-a", access) + if err != nil { + return err + } + if !strings.Contains(string(out), succDeleteUserMsg) { + return fmt.Errorf("failed to delete the user account") + } + + return nil +} + func changeBucketsOwner(s *S3Conf, buckets []string, owner string) error { for _, bucket := range buckets { out, err := execCommand("admin", "-a", s.awsID, "-s", s.awsSecret, "-er", s.endpoint, "change-bucket-owner", "-b", bucket, "-o", owner) diff --git a/runtests.sh b/runtests.sh index 2a21901..abb6320 100755 --- a/runtests.sh +++ b/runtests.sh @@ -20,8 +20,21 @@ if ! kill -0 $GW_PID; then fi # run tests +# full flow tests if ! ./versitygw test -a user -s pass -e http://127.0.0.1:7070 full-flow; then - echo "tests failed" + echo "full flow tests failed" + kill $GW_PID + exit 1 +fi +# posix tests +if ! ./versitygw test -a user -s pass -e http://127.0.0.1:7070 posix; then + echo "posix tests failed" + kill $GW_PID + exit 1 +fi +# iam tests +if ! ./versitygw test -a user -s pass -e http://127.0.0.1:7070 iam; then + echo "iam tests failed" kill $GW_PID exit 1 fi