diff --git a/.github/workflows/system.yml b/.github/workflows/system.yml index 8250ee8..f44c184 100644 --- a/.github/workflows/system.yml +++ b/.github/workflows/system.yml @@ -65,16 +65,16 @@ jobs: DELETE_BUCKETS_AFTER_TEST: "false" BACKEND: "posix" AWS_REGION: "us-east-1" - - set: "REST, posix, static, rest-put-bucket-tagging|rest-get-bucket-location, folder IAM" + - set: "REST, posix, static, rest-put-bucket-tagging|rest-get-bucket-location|rest-put-object-tagging, folder IAM" IAM_TYPE: folder - RUN_SET: "rest-put-bucket-tagging,rest-get-bucket-location" + RUN_SET: "rest-put-bucket-tagging,rest-get-bucket-location,rest-put-object-tagging" RECREATE_BUCKETS: "false" DELETE_BUCKETS_AFTER_TEST: "false" BACKEND: "posix" AWS_REGION: "us-east-1" - - set: "REST, posix, non-static, rest-put-bucket-tagging|rest-get-bucket-location, folder IAM" + - set: "REST, posix, non-static, rest-put-bucket-tagging|rest-get-bucket-location|rest-put-object-tagging, folder IAM" IAM_TYPE: folder - RUN_SET: "rest-put-bucket-tagging,rest-get-bucket-location" + RUN_SET: "rest-put-bucket-tagging,rest-get-bucket-location,rest-put-object-tagging" RECREATE_BUCKETS: "true" DELETE_BUCKETS_AFTER_TEST: "true" BACKEND: "posix" diff --git a/tests/drivers/get_object_tagging/get_object_tagging.sh b/tests/drivers/get_object_tagging/get_object_tagging.sh index ef200b6..3eca450 100644 --- a/tests/drivers/get_object_tagging/get_object_tagging.sh +++ b/tests/drivers/get_object_tagging/get_object_tagging.sh @@ -90,7 +90,7 @@ check_verify_object_tags() { return 1 fi elif [ "$1" == 'rest' ]; then - if ! parse_object_tags_rest; then + if ! parse_object_tags_rest "$TEST_FILE_FOLDER/object_tags.txt"; then log 2 "error parsing object tags" return 1 fi diff --git a/tests/drivers/get_object_tagging/get_object_tagging_rest.sh b/tests/drivers/get_object_tagging/get_object_tagging_rest.sh index d5b1ee1..66226f3 100644 --- a/tests/drivers/get_object_tagging/get_object_tagging_rest.sh +++ b/tests/drivers/get_object_tagging/get_object_tagging_rest.sh @@ -15,12 +15,48 @@ # under the License. parse_object_tags_rest() { - if ! tag_set_key=$(xmllint --xpath '//*[local-name()="Key"]/text()' "$TEST_FILE_FOLDER/object_tags.txt" 2>&1); then + if ! check_param_count_v2 "data file" 1 $#; then + return 1 + fi + log 5 "object tags: $(cat "$1")" + if ! tag_set_key=$(get_element_text "$1" "Tagging" "TagSet" "Tag" "Key" 2>&1); then log 2 "error getting key: $tag_set_key" return 1 fi - if ! tag_set_value=$(xmllint --xpath '//*[local-name()="Value"]/text()' "$TEST_FILE_FOLDER/object_tags.txt" 2>&1); then - log 2 "error getting value: $value" + if ! tag_set_value=$(get_element_text "$1" "Tagging" "TagSet" "Tag" "Value" 2>&1); then + log 2 "error getting value: $tag_set_value" + return 1 + fi + return 0 +} + +get_check_object_tags_single_set_go() { + if ! check_param_count_gt "bucket, key, expected tag key, expected tag value, params" 4 $#; then + return 1 + fi + expected_key="$3" + expected_value="$4" + if ! send_rest_go_command_callback "200" "check_object_tags_single_set" "-bucketName" "$1" "-objectKey" "$2" "-method" "GET" \ + "-query" "tagging=" "${@:5}"; then + log 2 "error sending go command or callback error" + return 1 + fi +} + +check_object_tags_single_set() { + if ! check_param_count_v2 "data file" 1 $#; then + return 1 + fi + if ! parse_object_tags_rest "$1"; then + log 2 "error parsing object tags" + return 1 + fi + if [ "$tag_set_key" != "$expected_key" ]; then + log 2 "key mismatch, expected '$expected_key', was '$tag_set_key'" + return 1 + fi + if [ "$tag_set_value" != "$expected_value" ]; then + log 2 "key mismatch, expected '$expected_value', was '$tag_set_value'" return 1 fi return 0 diff --git a/tests/drivers/put_object/put_object_rest.sh b/tests/drivers/put_object/put_object_rest.sh index f6d0566..f3e6e1f 100644 --- a/tests/drivers/put_object/put_object_rest.sh +++ b/tests/drivers/put_object/put_object_rest.sh @@ -64,3 +64,33 @@ send_openssl_go_command_chunked_no_content_length() { "-payloadType" "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" "-chunkSize" "8192" "-objectKey" "$2" assert_success } + +put_bucket_object_run_command() { + if ! check_param_count_gt "bucket, key, expected success val, params" 3 $#; then + return 1 + fi + if ! setup_bucket_and_add_file "$1" "$2"; then + log 2 "error setting up bucket and adding file" + return 1 + fi + if ! send_rest_go_command "$3" "-bucketName" "$1" "-objectKey" "$2" "${@:4}"; then + log 2 "error sending go command" + return 1 + fi + return 0 +} + +put_bucket_object_run_command_expect_error() { + if ! check_param_count_gt "bucket, key, expected response code, error code, message, params" 5 $#; then + return 1 + fi + if ! setup_bucket_and_add_file "$1" "$2"; then + log 2 "error setting up bucket and adding file" + return 1 + fi + if ! send_rest_go_command_expect_error "$3" "$4" "$5" "-bucketName" "$1" "-objectKey" "$2" "${@:6}"; then + log 2 "error sending go command and parsing error" + return 1 + fi + return 0 +} diff --git a/tests/drivers/put_object_tagging/put_object_tagging_rest.sh b/tests/drivers/put_object_tagging/put_object_tagging_rest.sh new file mode 100644 index 0000000..494858c --- /dev/null +++ b/tests/drivers/put_object_tagging/put_object_tagging_rest.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Copyright 2024 Versity Software +# This file is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +get_check_tag_error_with_invalid_key() { + if ! check_param_count_v2 "bucket name, key, tag key, tag value" 4 $#; then + return 1 + fi + invalid_key="$3" + if ! send_rest_go_command_expect_error_callback "400" "InvalidTag" "The TagKey you have provided is invalid" "check_invalid_key_error" \ + "-bucketName" "$1" "-objectKey" "$2" "-commandType" "putObjectTagging" "-tagKey" "$3" "-tagValue" "$4" "-contentMD5"; then + log 2 "error sending put tag command or checking callback" + return 1 + fi + return 0 +} + +check_invalid_key_error() { + if ! check_param_count_v2 "data file" 1 $#; then + return 1 + fi + if ! check_error_parameter "$1" "TagKey" "$invalid_key"; then + log 2 "error checking 'TagKey' parameter" + return 1 + fi + return 0 +} \ No newline at end of file diff --git a/tests/drivers/xml.sh b/tests/drivers/xml.sh index ab33dad..f018428 100644 --- a/tests/drivers/xml.sh +++ b/tests/drivers/xml.sh @@ -141,6 +141,21 @@ check_xml_error_contains() { return 0 } +check_xml_error_contains_with_single_error_field() { + if ! check_param_count_v2 "data source, expected error, string, expected key, expected value" 5 $#; then + return 1 + fi + if ! check_xml_error_contains "$1""$2" "$3"; then + log 2 "error checking initial xml error" + return 1 + fi + if ! check_error_parameter "$1" "$4" "$5"; then + log 2 "error checking error parameter" + return 1 + fi + return 0 +} + get_xml_data() { if ! check_param_count_v2 "data file, output file" 2 $#; then return 1 @@ -188,4 +203,5 @@ check_error_parameter() { log 2 "expected '$3', was '$unescaped_value'" return 1 fi + return 0 } diff --git a/tests/rest_scripts/command/putBucketTaggingCommand.go b/tests/rest_scripts/command/putBucketTaggingCommand.go index 5d2e04f..663fdf3 100644 --- a/tests/rest_scripts/command/putBucketTaggingCommand.go +++ b/tests/rest_scripts/command/putBucketTaggingCommand.go @@ -1,100 +1,25 @@ package command import ( - "encoding/xml" "errors" "fmt" - "strconv" ) -type Tag struct { - Key string `xml:"Key"` - Value string `xml:"Value"` -} - -type TagSet struct { - Tags []Tag `xml:"Tag"` -} - -type PutBucketTaggingTags struct { - XMLName xml.Name `xml:"Tagging"` - XMLNamespace string `xml:"xmlns,attr"` - TagSet TagSet `xml:"TagSet"` -} - -type PutBucketTaggingFields struct { - TagCount int - TagKeys []string - TagValues []string -} - type PutBucketTaggingCommand struct { - *S3Command - TagCount *int - Tags *PutBucketTaggingTags + *PutTaggingCommand } -func NewPutBucketTaggingCommand(s3Command *S3Command, fields *PutBucketTaggingFields) (*PutBucketTaggingCommand, error) { +func NewPutBucketTaggingCommand(s3Command *S3Command, fields *TaggingFields) (*PutBucketTaggingCommand, error) { if s3Command.BucketName == "" { return nil, errors.New("PutBucketTagging must have bucket name") } - s3Command.Method = "PUT" - s3Command.Query = "tagging=" command := &PutBucketTaggingCommand{ - S3Command: s3Command, + &PutTaggingCommand{ + S3Command: s3Command, + }, } - if len(fields.TagKeys) != len(fields.TagValues) { - return nil, errors.New("must be same number of tag keys and tag values") + if err := command.createTaggingPayload(fields); err != nil { + return nil, fmt.Errorf("error creating tagging payload: %w", err) } - if fields.TagCount > 0 && len(fields.TagKeys) != 0 { - return nil, errors.New("tagCount can not be set simultaneously with tagKeys or tagValues") - } - command.Tags = &PutBucketTaggingTags{ - XMLNamespace: "http://s3.amazonaws.com/doc/2006-03-01/", - } - if fields.TagCount > 0 { - command.Tags.GenerateKeyValuePairs(fields.TagCount) - - } else if len(fields.TagKeys) != 0 { - if err := command.Tags.AddTags(fields.TagKeys, fields.TagValues); err != nil { - return nil, fmt.Errorf("error adding keys and/or values to payload: %w", err) - } - } - xmlData, err := xml.Marshal(command.Tags) - if err != nil { - return nil, fmt.Errorf("error marshalling XML: %w", err) - } - command.Payload = "\n" + string(xmlData) return command, nil } - -func (p *PutBucketTaggingTags) GenerateKeyValuePairs(count int) { - p.TagSet.Tags = make([]Tag, 0, count) - for idx := 1; idx <= count; idx++ { - key := fmt.Sprintf("key%d", idx) - value := fmt.Sprintf("value%d", idx) - p.TagSet.Tags = append(p.TagSet.Tags, Tag{ - Key: key, - Value: value, - }) - } -} - -func (p *PutBucketTaggingTags) AddTags(keys, values []string) error { - p.TagSet.Tags = make([]Tag, 0, len(keys)) - for idx, key := range keys { - unquotedKey, err := strconv.Unquote(`"` + key + `"`) - if err != nil { - return fmt.Errorf("error unquoting key: %w", err) - } - unquotedValue, err := strconv.Unquote(`"` + values[idx] + `"`) - if err != nil { - return fmt.Errorf("error unquoting key: %w", err) - } - p.TagSet.Tags = append(p.TagSet.Tags, Tag{ - Key: unquotedKey, - Value: unquotedValue, - }) - } - return nil -} diff --git a/tests/rest_scripts/command/putObjectTaggingCommand.go b/tests/rest_scripts/command/putObjectTaggingCommand.go new file mode 100644 index 0000000..c986cce --- /dev/null +++ b/tests/rest_scripts/command/putObjectTaggingCommand.go @@ -0,0 +1,28 @@ +package command + +import ( + "errors" + "fmt" +) + +type PutObjectTaggingCommand struct { + *PutTaggingCommand +} + +func NewPutObjectTaggingCommand(s3Command *S3Command, fields *TaggingFields) (*PutObjectTaggingCommand, error) { + if s3Command.BucketName == "" { + return nil, errors.New("PutObjectTagging must have bucket name") + } + if s3Command.ObjectKey == "" { + return nil, errors.New("PutObjectTagging must have object key") + } + command := &PutObjectTaggingCommand{ + &PutTaggingCommand{ + S3Command: s3Command, + }, + } + if err := command.createTaggingPayload(fields); err != nil { + return nil, fmt.Errorf("error creating tagging payload: %w", err) + } + return command, nil +} diff --git a/tests/rest_scripts/command/putTaggingCommand.go b/tests/rest_scripts/command/putTaggingCommand.go new file mode 100644 index 0000000..797df66 --- /dev/null +++ b/tests/rest_scripts/command/putTaggingCommand.go @@ -0,0 +1,41 @@ +package command + +import ( + "encoding/xml" + "errors" + "fmt" +) + +type PutTaggingCommand struct { + *S3Command + TagCount *int + Tags *Tagging +} + +func (p *PutTaggingCommand) createTaggingPayload(fields *TaggingFields) error { + p.Method = "PUT" + p.Query = "tagging=" + if len(fields.TagKeys) != len(fields.TagValues) { + return errors.New("must be same number of tag keys and tag values") + } + if fields.TagCount > 0 && len(fields.TagKeys) != 0 { + return errors.New("tagCount can not be set simultaneously with tagKeys or tagValues") + } + p.Tags = &Tagging{ + XMLNamespace: "https://s3.amazonaws.com/doc/2006-03-01/", + } + if fields.TagCount > 0 { + p.Tags.GenerateKeyValuePairs(fields.TagCount) + + } else if len(fields.TagKeys) != 0 { + if err := p.Tags.AddTags(fields.TagKeys, fields.TagValues); err != nil { + return fmt.Errorf("error adding keys and/or values to payload: %w", err) + } + } + xmlData, err := xml.Marshal(p.Tags) + if err != nil { + return fmt.Errorf("error marshalling XML: %w", err) + } + p.Payload = "\n" + string(xmlData) + return nil +} diff --git a/tests/rest_scripts/command/tagging.go b/tests/rest_scripts/command/tagging.go new file mode 100644 index 0000000..b018bd2 --- /dev/null +++ b/tests/rest_scripts/command/tagging.go @@ -0,0 +1,59 @@ +package command + +import ( + "encoding/xml" + "fmt" + "strconv" +) + +type Tag struct { + Key string `xml:"Key"` + Value string `xml:"Value"` +} + +type TagSet struct { + Tags []Tag `xml:"Tag"` +} + +type Tagging struct { + XMLName xml.Name `xml:"Tagging"` + XMLNamespace string `xml:"xmlns,attr"` + TagSet TagSet `xml:"TagSet"` +} + +type TaggingFields struct { + TagCount int + TagKeys []string + TagValues []string +} + +func (p *Tagging) AddTags(keys, values []string) error { + p.TagSet.Tags = make([]Tag, 0, len(keys)) + for idx, key := range keys { + unquotedKey, err := strconv.Unquote(`"` + key + `"`) + if err != nil { + return fmt.Errorf("error unquoting key: %w", err) + } + unquotedValue, err := strconv.Unquote(`"` + values[idx] + `"`) + if err != nil { + return fmt.Errorf("error unquoting key: %w", err) + } + p.TagSet.Tags = append(p.TagSet.Tags, Tag{ + Key: unquotedKey, + Value: unquotedValue, + }) + } + return nil +} + +func (p *Tagging) GenerateKeyValuePairs(count int) { + p.TagSet.Tags = make([]Tag, 0, count) + for idx := 1; idx <= count; idx++ { + key := fmt.Sprintf("key%d", idx) + value := fmt.Sprintf("value%d", idx) + p.TagSet.Tags = append(p.TagSet.Tags, Tag{ + Key: key, + Value: value, + }) + } +} diff --git a/tests/rest_scripts/generateCommand.go b/tests/rest_scripts/generateCommand.go index 49f0a82..40d8675 100644 --- a/tests/rest_scripts/generateCommand.go +++ b/tests/rest_scripts/generateCommand.go @@ -14,6 +14,7 @@ const ( CreateBucket = "createBucket" PutBucketTagging = "putBucketTagging" PutObject = "putObject" + PutObjectTagging = "putObjectTagging" ) var method *string @@ -150,7 +151,7 @@ func getS3CommandType(baseCommand *command.S3Command) (command.S3CommandConverte return nil, fmt.Errorf("error setting up CreateBucket command: %v", err) } case PutBucketTagging: - fields := command.PutBucketTaggingFields{ + fields := command.TaggingFields{ TagCount: *tagCount, TagKeys: tagKeys, TagValues: tagValues, @@ -162,6 +163,15 @@ func getS3CommandType(baseCommand *command.S3Command) (command.S3CommandConverte if s3Command, err = command.NewPutObjectCommand(baseCommand); err != nil { return nil, fmt.Errorf("error setting up PutObject command: %v", err) } + case PutObjectTagging: + fields := command.TaggingFields{ + TagCount: *tagCount, + TagKeys: tagKeys, + TagValues: tagValues, + } + if s3Command, err = command.NewPutObjectTaggingCommand(baseCommand, &fields); err != nil { + return nil, fmt.Errorf("error setting up PutBucketTagging command: %v", err) + } default: s3Command = baseCommand } diff --git a/tests/run.sh b/tests/run.sh index 00d7623..9053412 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -48,6 +48,7 @@ show_help() { echo " rest-not-implemented Run REST multipart tests" echo " rest-put-bucket-tagging Run REST put-bucket-tagging tests" echo " rest-put-object Run REST put-object tests" + echo " rest-put-object-tagging Run REST put-object-tagging tests" echo " rest-versioning Run REST versioning tests" echo " rest-bucket Run REST bucket tests" } @@ -63,7 +64,8 @@ handle_param() { s3api-bucket|s3api-object|s3api-multipart|rest-base|rest-acl|rest-chunked|rest-checksum|\ rest-create-bucket|rest-head-bucket|rest-list-buckets|rest-not-implemented|\ rest-put-object|rest-versioning|rest-bucket|rest-multipart|rest-delete-bucket-ownership-controls|\ - rest-delete-bucket-tagging|setup-remove-static|rest-put-bucket-tagging|rest-get-bucket-location) + rest-delete-bucket-tagging|setup-remove-static|rest-put-bucket-tagging|rest-get-bucket-location|\ + rest-put-object-tagging) run_suite "$1" ;; *) # Handle unrecognized options or positional arguments @@ -188,6 +190,8 @@ run_suite() { exit_code=1 elif ! "$HOME"/bin/bats ./tests/test_rest_put_object.sh; then exit_code=1 + elif ! "$HOME"/bin/bats ./tests/test_rest_put_object_tagging.sh; then + exit_code=1 elif ! "$HOME"/bin/bats ./tests/test_rest_versioning.sh; then exit_code=1 elif ! "$HOME"/bin/bats ./tests/test_rest_bucket.sh; then @@ -254,6 +258,10 @@ run_suite() { echo "Running REST put-object tests ..." "$HOME"/bin/bats ./tests/test_rest_put_object.sh || exit_code=$? ;; + rest-put-object-tagging) + echo "Running REST put-object-tagging tests ..." + "$HOME"/bin/bats ./tests/test_rest_put_object_tagging.sh || exit_code=$? + ;; rest-versioning) echo "Running REST versioning tests ..." "$HOME"/bin/bats ./tests/test_rest_versioning.sh || exit_code=$? diff --git a/tests/test_rest_put_object.sh b/tests/test_rest_put_object.sh index 33add88..5cf5cc6 100755 --- a/tests/test_rest_put_object.sh +++ b/tests/test_rest_put_object.sh @@ -22,6 +22,7 @@ source ./tests/drivers/file.sh source ./tests/drivers/create_bucket/create_bucket_rest.sh source ./tests/drivers/head_object/head_object_rest.sh source ./tests/drivers/put_object/put_object_rest.sh +source ./tests/util/util_public_access_block.sh test_file="test_file" export RUN_USERS=true @@ -443,3 +444,32 @@ export RUN_USERS=true "-payloadType" "STREAMING-UNSIGNED-PAYLOAD-TRAILER" "-chunkSize" "8192" assert_success } + +@test "REST - PutObjectTagging - invalid x-amz-request-payer" { + run get_bucket_name "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run create_versitygw_acl_user_or_get_direct_user "$USERNAME_ONE" "$PASSWORD_ONE" + assert_success + username=${lines[2]} + password=${lines[3]} + + run setup_bucket_and_file_v2 "$bucket_name" "$test_file" + assert_success + + run put_bucket_ownership_controls "$bucket_name" "BucketOwnerPreferred" + assert_success + + if [ "$DIRECT" == "true" ]; then + run allow_public_access "$bucket_name" + assert_success + fi + + run put_canned_acl_rest "$bucket_name" "public-read-write" + assert_success + + run send_rest_go_command "200" "-bucketName" "$bucket_name" "-objectKey" "$test_file" "-payloadFile" "$TEST_FILE_FOLDER/$test_file" \ + "-method" "PUT" "-contentMD5" "-awsAccessKeyId" "$username" "-awsSecretAccessKey" "$password" "-signedParams" "x-amz-request-payer:dummy" + assert_success +} diff --git a/tests/test_rest_put_object_tagging.sh b/tests/test_rest_put_object_tagging.sh new file mode 100755 index 0000000..76fb3d4 --- /dev/null +++ b/tests/test_rest_put_object_tagging.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bats + +# Copyright 2025 Versity Software +# This file is licensed under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +load ./bats-support/load +load ./bats-assert/load + +source ./tests/drivers/create_bucket/create_bucket_rest.sh +source ./tests/drivers/get_object_tagging/get_object_tagging_rest.sh +source ./tests/drivers/put_object_tagging/put_object_tagging_rest.sh +source ./tests/util/util_public_access_block.sh +source ./tests/setup.sh + +test_file="test_file" + +@test "REST - PutObjectTagging - content-md5 not required for object tagging" { + run get_bucket_name "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run put_bucket_object_run_command "$bucket_name" "$test_file" "200" "-commandType" "putObjectTagging" "-tagKey" "key" "-tagValue" "value" + assert_success + + run get_check_object_tags_single_set_go "$bucket_name" "$test_file" "key" "value" + assert_success +} + +@test "REST - PutObjectTagging - invalid key returns invalid key in error" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/1663" + fi + run get_bucket_name "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run setup_bucket_and_add_file "$bucket_name" "$test_file" + assert_success + + run get_check_tag_error_with_invalid_key "$bucket_name" "$test_file" "ke&y" "value" + assert_success +} + +@test "REST - PutObjectTagging - success with content-md5" { + run get_bucket_name "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run put_bucket_object_run_command "$bucket_name" "$test_file" "200" "-commandType" "putObjectTagging" "-tagKey" "key" "-tagValue" "value" "-contentMD5" + assert_success + + run get_check_object_tags_single_set_go "$bucket_name" "$test_file" "key" "value" + assert_success +} + +@test "REST - PutObjectTagging - mismatched bucket owner" { + run get_bucket_name "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run put_bucket_object_run_command_expect_error "$bucket_name" "$test_file" "403" "AccessDenied" "Access Denied" \ + "-commandType" "putObjectTagging" "-tagKey" "key" "-tagValue" "value" "-contentMD5" "-signedParams" "x-amz-expected-bucket-owner:012345678901" + assert_success +}