diff --git a/tests/.env.docker.default b/tests/.env.docker.default index bd15d88..2ac0463 100644 --- a/tests/.env.docker.default +++ b/tests/.env.docker.default @@ -13,8 +13,8 @@ SECRETS_FILE=./tests/.secrets MC_ALIAS=versity LOG_LEVEL=2 USERS_FOLDER=$PWD/iam -#TEST_LOG_FILE=test.log -#VERSITY_LOG_FILE=versity.log +#TEST_LOG_FILE=log/test.log +#VERSITY_LOG_FILE=log/versity.log IAM_TYPE=folder DIRECT=false #DIRECT_DISPLAY_NAME= diff --git a/tests/Dockerfile_test_bats b/tests/Dockerfile_test_bats index a9f3c67..1509e3a 100644 --- a/tests/Dockerfile_test_bats +++ b/tests/Dockerfile_test_bats @@ -89,5 +89,7 @@ ENV WORKSPACE=. ENV VERSITYGW_TEST_ENV=$CONFIG_FILE #ENV AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED +RUN mkdir log + ENTRYPOINT ["tests/run.sh"] CMD ["s3api,s3,s3cmd,mc,rest"] diff --git a/tests/README.md b/tests/README.md index 7e1738d..b3e3e1b 100644 --- a/tests/README.md +++ b/tests/README.md @@ -74,7 +74,7 @@ To communicate directly with s3, in order to compare the gateway results to dire 1. Copy `.secrets.default` to `.secrets` in the `tests` folder and change the parameters and add the additional s3 fields explained in the **S3 Backend** section above if running with the s3 backend. 2. By default, the dockerfile uses the **arm** architecture (usually modern Mac). If using **amd** (usually earlier Mac or Linux), you can either replace the corresponding `ARG` values directly, or with `arg="="` Also, you can determine which is used by your OS with `uname -a`. -3. Build and run the `Dockerfile_test_bats` file. Change the `SECRETS_FILE` and `CONFIG_FILE` parameters to point to your secrets and config file, respectively, if not using the defaults. Example: `docker build -t -f Dockerfile_test_bats --build-arg="SECRETS_FILE=" --build-arg="CONFIG_FILE=" .`. +3. Build and run the `Dockerfile_test_bats` file. Change the `SECRETS_FILE` and `CONFIG_FILE` parameters to point to your secrets and config file, respectively, if not using the defaults. Example: `docker build -t --build-arg="SECRETS_FILE=" --build-arg="CONFIG_FILE=" -f tests/Dockerfile_test_bats .`. 4. To run the entire suite, run `docker run -it `. To run an individual suite, pass in the name of the suite as defined in `tests/run.sh` (e.g. REST tests -> `docker run -it rest`). Also, multiple specific suites can be run, if separated by comma. ## Instructions - Running with docker-compose diff --git a/tests/drivers/not_implemented/not_implemented_rest.sh b/tests/drivers/not_implemented/not_implemented_rest.sh index 72e9391..c620b80 100644 --- a/tests/drivers/not_implemented/not_implemented_rest.sh +++ b/tests/drivers/not_implemented/not_implemented_rest.sh @@ -21,7 +21,7 @@ send_not_implemented_expect_failure() { log 2 "'send_not_implemented_expect_failure' param count must be multiple of 2 (key/value pairs)" return 1 fi - if ! curl_command=$(go run ./tests/rest_scripts/generate_command.go -awsAccessKeyId "$AWS_ACCESS_KEY_ID" -awsSecretAccessKey "$AWS_SECRET_ACCESS_KEY" -url "$AWS_ENDPOINT_URL" "$@" 2>&1); then + if ! curl_command=$(go run ./tests/rest_scripts/generateCommand.go -awsAccessKeyId "$AWS_ACCESS_KEY_ID" -awsSecretAccessKey "$AWS_SECRET_ACCESS_KEY" -url "$AWS_ENDPOINT_URL" "$@" 2>&1); then log 2 "error: $curl_command" return 1 fi diff --git a/tests/drivers/openssl.sh b/tests/drivers/openssl.sh index 30de786..a92a675 100644 --- a/tests/drivers/openssl.sh +++ b/tests/drivers/openssl.sh @@ -92,3 +92,33 @@ send_via_openssl_with_timeout() { fi return 0 } + +send_openssl_go_command_expect_error() { + if ! check_param_count_gt "expected HTTP code, expected error code, expected message, params" 4 $#; then + return 1 + fi + if ! go run "./tests/rest_scripts/generateCommand.go" "-awsAccessKeyId" "$AWS_ACCESS_KEY_ID" "-awsSecretAccessKey" "$AWS_SECRET_ACCESS_KEY" "-url" "$AWS_ENDPOINT_URL" "-client" "openssl" "-filePath" "$TEST_FILE_FOLDER/openssl_command.txt" "${@:4}"; then + log 2 "error sending go command and checking error" + return 1 + fi + if ! send_via_openssl_check_code_error_contains "$TEST_FILE_FOLDER/openssl_command.txt" "$1" "$2" "$3"; then + log 2 "error sending via openssl" + return 1 + fi + return 0 +} + +send_openssl_go_command() { + if ! check_param_count_gt "expected HTTP code, params" 2 $#; then + return 1 + fi + if ! go run "./tests/rest_scripts/generateCommand.go" "-awsAccessKeyId" "$AWS_ACCESS_KEY_ID" "-awsSecretAccessKey" "$AWS_SECRET_ACCESS_KEY" "-url" "$AWS_ENDPOINT_URL" "-client" "openssl" "-filePath" "$TEST_FILE_FOLDER/openssl_command.txt" "${@:2}"; then + log 2 "error sending go command and checking error" + return 1 + fi + if ! result=$(send_via_openssl_and_check_code "$TEST_FILE_FOLDER/openssl_command.txt" "$1" 2>&1); then + log 2 "error sending via openssl and checking code: $result" + return 1 + fi + return 0 +} diff --git a/tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh b/tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh new file mode 100644 index 0000000..c69191d --- /dev/null +++ b/tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bats + +# 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. + +send_put_bucket_tagging_command_check_invalid_content_md5() { + if ! check_param_count_v2 "bucket name" 1 $#; then + return 1 + fi + invalid_content_md5="dummy" + if ! send_rest_go_command_expect_error_callback "400" "InvalidDigest" "was invalid" "check_invalid_content_md5" "-bucketName" "$1" "-query" "tagging=" "-method" "PUT" "-signedParams" "Content-MD5:$invalid_content_md5" \ + "-payload" "keyvalue"; then + log 2 "error sending command and checking callback" + return 1 + fi + return 0 +} + +check_invalid_content_md5() { + if ! check_param_count_v2 "data file" 1 $#; then + return 1 + fi + # shellcheck disable=SC2154 + if ! returned_content_md5=$(get_element_text "$1" "Error" "Content-MD5"); then + log 2 "error getting argument name" + return 1 + fi + if [ "$returned_content_md5" != "$invalid_content_md5" ]; then + log 2 "expected '$invalid_content_md5', was '$returned_content_md5'" + return 1 + fi + return 0 +} \ No newline at end of file diff --git a/tests/drivers/rest.sh b/tests/drivers/rest.sh index 14cee9c..6750b64 100644 --- a/tests/drivers/rest.sh +++ b/tests/drivers/rest.sh @@ -170,7 +170,7 @@ send_rest_command_expect_success_callback() { } rest_go_command_perform_send() { - if ! curl_command=$(go run ./tests/rest_scripts/generate_command.go -awsAccessKeyId "$AWS_ACCESS_KEY_ID" -awsSecretAccessKey "$AWS_SECRET_ACCESS_KEY" -url "$AWS_ENDPOINT_URL" "$@" 2>&1); then + if ! curl_command=$(go run ./tests/rest_scripts/generateCommand.go -awsAccessKeyId "$AWS_ACCESS_KEY_ID" -awsSecretAccessKey "$AWS_SECRET_ACCESS_KEY" -url "$AWS_ENDPOINT_URL" "$@" 2>&1); then log 2 "error: $curl_command" return 1 fi diff --git a/tests/rest_scripts/generate_command.go b/tests/rest_scripts/command/s3Command.go similarity index 51% rename from tests/rest_scripts/generate_command.go rename to tests/rest_scripts/command/s3Command.go index 0b86178..a7a67ec 100644 --- a/tests/rest_scripts/generate_command.go +++ b/tests/rest_scripts/command/s3Command.go @@ -1,35 +1,20 @@ -package main +package command import ( "crypto/hmac" + "crypto/md5" "crypto/sha256" + "encoding/base64" "encoding/hex" - "flag" + "errors" "fmt" - "log" + logger "github.com/versity/versitygw/tests/rest_scripts/logger" + "os" "sort" "strings" "time" ) -var method *string -var url *string -var bucketName *string -var objectKey *string -var query *string -var awsRegion *string -var awsAccessKeyId *string -var awsSecretAccessKey *string -var serviceName *string -var debug *bool -var signedParamsMap restParams -var payloadFile *string -var incorrectSignature *bool -var incorrectCredential *string -var authorizationScheme *string -var incorrectYearMonthDay *bool -var invalidYearMonthDay *bool - type S3Command struct { Method string Url string @@ -48,6 +33,13 @@ type S3Command struct { IncorrectCredential string IncorrectYearMonthDay bool InvalidYearMonthDay bool + Payload string + ContentMD5 bool + IncorrectContentMD5 bool + MissingHostParam bool + FilePath string + CustomHostParam string + CustomHostParamSet bool currentDateTime string host string @@ -60,100 +52,30 @@ type S3Command struct { signature string } -type restParams map[string]string - -func (r *restParams) String() string { - return fmt.Sprintf("%v", *r) -} - -func (r *restParams) Set(value string) error { - *r = make(map[string]string) - pairs := strings.Split(value, ",") - for _, pair := range pairs { - kv := strings.SplitN(pair, ":", 2) - if len(kv) != 2 { - } - if len(kv) != 2 { - return fmt.Errorf("invalid key-value pair: %s", pair) - } - (*r)[kv[0]] = kv[1] +func (s *S3Command) OpenSSLCommand() error { + if s.FilePath == "" { + return errors.New("for openssl command, filePath must be set") + } + if err := s.prepareForBuild(); err != nil { + return fmt.Errorf("error preparing for command building: %w", err) + } + if err := s.buildOpenSSLCommand(); err != nil { + return fmt.Errorf("error building openSSL command: %w", err) } return nil } -func main() { - if err := checkFlags(); err != nil { - log.Fatalf("Error checking flags: %v", err) - } - - s3Command := &S3Command{ - Method: *method, - Url: *url, - BucketName: *bucketName, - ObjectKey: *objectKey, - Query: *query, - AwsRegion: *awsRegion, - AwsAccessKeyId: *awsAccessKeyId, - AwsSecretAccessKey: *awsSecretAccessKey, - ServiceName: *serviceName, - SignedParams: signedParamsMap, - PayloadFile: *payloadFile, - IncorrectSignature: *incorrectSignature, - AuthorizationScheme: *authorizationScheme, - IncorrectCredential: *incorrectCredential, - IncorrectYearMonthDay: *incorrectYearMonthDay, - InvalidYearMonthDay: *invalidYearMonthDay, - } - curlShellCommand, err := s3Command.CurlShellCommand() - if err != nil { - log.Fatalf("Error generating curl command: %v", err) - } - fmt.Println(curlShellCommand) -} - -func checkFlags() error { - method = flag.String("method", "GET", "HTTP method to use") - url = flag.String("url", "https://localhost:7070", "S3 server URL") - bucketName = flag.String("bucketName", "", "Bucket name") - objectKey = flag.String("objectKey", "", "Object key") - query = flag.String("query", "", "S3 query") - awsAccessKeyId = flag.String("awsAccessKeyId", "", "AWS access key ID") - awsSecretAccessKey = flag.String("awsSecretAccessKey", "", "AWS secret access key") - awsRegion = flag.String("awsRegion", "us-east-1", "AWS region") - serviceName = flag.String("serviceName", "s3", "Service name") - debug = flag.Bool("debug", false, "Print debug statements") - flag.Var(&signedParamsMap, "signedParams", "Signed params, separated by comma") - payloadFile = flag.String("payloadFile", "", "Payload file path, if any") - incorrectSignature = flag.Bool("incorrectSignature", false, "Simulate an incorrect signature") - incorrectYearMonthDay = flag.Bool("incorrectYearMonthDay", false, "Simulate an incorrect year/month/day") - invalidYearMonthDay = flag.Bool("invalidYearMonthDay", false, "Simulate an invalid year/month/day") - incorrectCredential = flag.String("incorrectCredential", "", "Add an incorrect credential string") - authorizationScheme = flag.String("authorizationScheme", "AWS4-HMAC-SHA256", "Authorization Scheme") - // Parse the flags - flag.Parse() - - if flag.Lookup("awsAccessKeyId").Value.String() == "" { - return fmt.Errorf("the 'awsAccessKeyId' value must be set") - } - if flag.Lookup("awsSecretAccessKey").Value.String() == "" { - return fmt.Errorf("the 'awsSecretAccessKey' value must be set") - } - return nil -} - -func printDebug(format string, args ...any) { - if *debug { - log.Printf(format, args...) - } -} - -func hmacSHA256(key []byte, data string) []byte { - h := hmac.New(sha256.New, key) - h.Write([]byte(data)) - return h.Sum(nil) -} - func (s *S3Command) CurlShellCommand() (string, error) { + if err := s.prepareForBuild(); err != nil { + return "", fmt.Errorf("error preparing for command building: %w", err) + } + return s.buildCurlShellCommand() +} + +func (s *S3Command) prepareForBuild() error { + if s.PayloadFile != "" && s.Payload != "" { + return fmt.Errorf("cannot have both payload and payloadFile parameters set") + } if s.IncorrectYearMonthDay { s.currentDateTime = time.Now().Add(-48 * time.Hour).UTC().Format("20060102T150405Z") } else { @@ -161,22 +83,13 @@ func (s *S3Command) CurlShellCommand() (string, error) { } protocolAndHost := strings.Split(s.Url, "://") if len(protocolAndHost) != 2 { - return "", fmt.Errorf("invalid URL value: %s", s.Url) + return fmt.Errorf("invalid URL value: %s", s.Url) } s.host = protocolAndHost[1] s.payloadHash = "UNSIGNED-PAYLOAD" - s.headerValues = [][]string{ - {"host", s.host}, - {"x-amz-content-sha256", s.payloadHash}, - {"x-amz-date", s.currentDateTime}, + if err := s.addHeaderValues(); err != nil { + return fmt.Errorf("error adding header values: %w", err) } - for key, value := range s.SignedParams { - s.headerValues = append(s.headerValues, []string{key, value}) - } - sort.Slice(s.headerValues, - func(i, j int) bool { - return s.headerValues[i][0] < s.headerValues[j][0] - }) s.path = "/" + s.BucketName if s.ObjectKey != "" { s.path += "/" + s.ObjectKey @@ -185,19 +98,73 @@ func (s *S3Command) CurlShellCommand() (string, error) { s.yearMonthDay = strings.Split(s.currentDateTime, "T")[0] if s.InvalidYearMonthDay { - s.yearMonthDay = s.yearMonthDay[:len(s.yearMonthDay)-1] + s.yearMonthDay = s.yearMonthDay[:len(s.yearMonthDay)-2] } s.getStsSignature() + return nil +} - return s.buildCurlShellCommand(), nil +func (s *S3Command) addHeaderValues() error { + s.headerValues = [][]string{} + if s.MissingHostParam { + s.headerValues = append(s.headerValues, []string{"host", ""}) + } else if s.CustomHostParamSet { + s.headerValues = append(s.headerValues, []string{"host", s.CustomHostParam}) + } else { + s.headerValues = append(s.headerValues, []string{"host", s.host}) + } + s.headerValues = append(s.headerValues, + []string{"x-amz-content-sha256", s.payloadHash}, + []string{"x-amz-date", s.currentDateTime}, + ) + for key, value := range s.SignedParams { + s.headerValues = append(s.headerValues, []string{key, value}) + } + if s.ContentMD5 || s.IncorrectContentMD5 { + if err := s.addContentMD5Header(); err != nil { + return fmt.Errorf("error adding Content-MD5 header: %w", err) + } + } + sort.Slice(s.headerValues, + func(i, j int) bool { + return s.headerValues[i][0] < s.headerValues[j][0] + }) + return nil +} + +func (s *S3Command) addContentMD5Header() error { + var payloadData []byte + var err error + if s.PayloadFile != "" { + if payloadData, err = os.ReadFile(s.PayloadFile); err != nil { + return fmt.Errorf("error reading file %s: %w", s.PayloadFile, err) + } + } else { + logger.PrintDebug("Payload: %s", s.Payload) + payloadData = []byte(strings.Replace(s.Payload, "\\", "", -1)) + } + + hasher := md5.New() + hasher.Write(payloadData) + md5Hash := hasher.Sum(nil) + if s.IncorrectContentMD5 { + if md5Hash[0] == 'a' { + md5Hash[0] = 'A' + } else { + md5Hash[0] = 'a' + } + } + contentMD5 := base64.StdEncoding.EncodeToString(md5Hash) + + s.headerValues = append(s.headerValues, []string{"Content-MD5", contentMD5}) + return nil } func (s *S3Command) generateCanonicalRequestString() { - canonicalRequestLines := []string{*method} + canonicalRequestLines := []string{s.Method} canonicalRequestLines = append(canonicalRequestLines, s.path) canonicalRequestLines = append(canonicalRequestLines, s.Query) - //canonicalRequestLines = append(canonicalRequestLines, "host:"+s.host) var signedParams []string for _, headerValue := range s.headerValues { @@ -211,7 +178,7 @@ func (s *S3Command) generateCanonicalRequestString() { canonicalRequestLines = append(canonicalRequestLines, s.signedParamString, s.payloadHash) canonicalRequestString := strings.Join(canonicalRequestLines, "\n") - printDebug("Canonical request string: %s\n", canonicalRequestString) + logger.PrintDebug("Canonical request string: %s\n", canonicalRequestString) canonicalRequestHashBytes := sha256.Sum256([]byte(canonicalRequestString)) s.canonicalRequestHash = hex.EncodeToString(canonicalRequestHashBytes[:]) @@ -247,32 +214,69 @@ func (s *S3Command) getStsSignature() { s.signature = hex.EncodeToString(signatureBytes) } -func (s *S3Command) buildCurlShellCommand() string { +func (s *S3Command) buildCurlShellCommand() (string, error) { + if s.MissingHostParam { + return "", fmt.Errorf("missingHostParam option only available for OpenSSL commands") + } curlCommand := []string{"curl", "-iks"} if s.Method != "GET" { curlCommand = append(curlCommand, fmt.Sprintf("-X %s ", s.Method)) } - fullPath := "\"" + *url + s.path - if *query != "" { - fullPath += "?" + *query + fullPath := "\"" + s.Url + s.path + if s.Query != "" { + fullPath += "?" + s.Query } fullPath += "\"" curlCommand = append(curlCommand, fullPath) - var credentialString string - if s.IncorrectCredential == "" { - credentialString = fmt.Sprintf("%s/%s/%s/%s/aws4_request", s.AwsAccessKeyId, s.yearMonthDay, s.AwsRegion, s.ServiceName) - } else { - credentialString = s.IncorrectCredential - } - authorizationString := fmt.Sprintf("\"Authorization: %s Credential=%s,SignedHeaders=%s,Signature=%s\"", - s.AuthorizationScheme, credentialString, s.signedParamString, s.signature) - curlCommand = append(curlCommand, "-H", authorizationString) + authorizationString := s.buildAuthorizationString() + curlCommand = append(curlCommand, "-H", fmt.Sprintf("\"%s\"", authorizationString)) for _, headerValue := range s.headerValues { headerString := fmt.Sprintf("\"%s: %s\"", headerValue[0], headerValue[1]) curlCommand = append(curlCommand, "-H", headerString) } if s.PayloadFile != "" { curlCommand = append(curlCommand, "-T", s.PayloadFile) + } else if s.Payload != "" { + curlCommand = append(curlCommand, "-H", "\"Content-Type: application/xml\"", "-d", fmt.Sprintf("\"%s\"", s.Payload)) } - return strings.Join(curlCommand, " ") + return strings.Join(curlCommand, " "), nil +} + +func (s *S3Command) buildAuthorizationString() string { + var credentialString string + if s.IncorrectCredential == "" { + credentialString = fmt.Sprintf("%s/%s/%s/%s/aws4_request", s.AwsAccessKeyId, s.yearMonthDay, s.AwsRegion, s.ServiceName) + } else { + credentialString = s.IncorrectCredential + } + return fmt.Sprintf("Authorization: %s Credential=%s,SignedHeaders=%s,Signature=%s", + s.AuthorizationScheme, credentialString, s.signedParamString, s.signature) +} + +func (s *S3Command) buildOpenSSLCommand() error { + openSSLCommand := []string{fmt.Sprintf("%s %s HTTP/1.1", s.Method, s.path)} + openSSLCommand = append(openSSLCommand, s.buildAuthorizationString()) + for _, headerValue := range s.headerValues { + if headerValue[0] == "host" && s.MissingHostParam { + continue + } + openSSLCommand = append(openSSLCommand, fmt.Sprintf("%s:%s", headerValue[0], headerValue[1])) + } + openSSLCommand = append(openSSLCommand, "\r\n") + var file *os.File + var err error + if file, err = os.Create(s.FilePath); err != nil { + return fmt.Errorf("error opening file: %w", err) + } + openSSLCommandBytes := []byte(strings.Join(openSSLCommand, "\r\n")) + if _, err = file.Write(openSSLCommandBytes); err != nil { + return fmt.Errorf("error writing to file: %w", err) + } + return nil +} + +func hmacSHA256(key []byte, data string) []byte { + h := hmac.New(sha256.New, key) + h.Write([]byte(data)) + return h.Sum(nil) } diff --git a/tests/rest_scripts/generateCommand.go b/tests/rest_scripts/generateCommand.go new file mode 100644 index 0000000..c0529ac --- /dev/null +++ b/tests/rest_scripts/generateCommand.go @@ -0,0 +1,152 @@ +package main + +import ( + "flag" + "fmt" + "github.com/versity/versitygw/tests/rest_scripts/command" + logger "github.com/versity/versitygw/tests/rest_scripts/logger" + "log" + "strings" +) + +const ( + CURL = "curl" + OPENSSL = "openssl" +) + +var method *string +var url *string +var bucketName *string +var objectKey *string +var query *string +var awsRegion *string +var awsAccessKeyId *string +var awsSecretAccessKey *string +var serviceName *string + +var signedParamsMap restParams +var payloadFile *string +var incorrectSignature *bool +var incorrectCredential *string +var authorizationScheme *string +var incorrectYearMonthDay *bool +var invalidYearMonthDay *bool +var payload *string +var contentMD5 *bool +var incorrectContentMD5 *bool +var missingHostParam *bool +var filePath *string +var client *string +var customHostParam *string +var customHostParamSet bool = false + +type restParams map[string]string + +func (r *restParams) String() string { + return fmt.Sprintf("%v", *r) +} + +func (r *restParams) Set(value string) error { + *r = make(map[string]string) + pairs := strings.Split(value, ",") + for _, pair := range pairs { + kv := strings.SplitN(pair, ":", 2) + if len(kv) != 2 { + } + if len(kv) != 2 { + return fmt.Errorf("invalid key-value pair: %s", pair) + } + (*r)[kv[0]] = kv[1] + } + return nil +} + +func main() { + if err := checkFlags(); err != nil { + log.Fatalf("Error checking flags: %v", err) + } + + s3Command := &command.S3Command{ + Method: *method, + Url: *url, + BucketName: *bucketName, + ObjectKey: *objectKey, + Query: *query, + AwsRegion: *awsRegion, + AwsAccessKeyId: *awsAccessKeyId, + AwsSecretAccessKey: *awsSecretAccessKey, + ServiceName: *serviceName, + SignedParams: signedParamsMap, + PayloadFile: *payloadFile, + IncorrectSignature: *incorrectSignature, + AuthorizationScheme: *authorizationScheme, + IncorrectCredential: *incorrectCredential, + IncorrectYearMonthDay: *incorrectYearMonthDay, + InvalidYearMonthDay: *invalidYearMonthDay, + Payload: *payload, + ContentMD5: *contentMD5, + IncorrectContentMD5: *incorrectContentMD5, + MissingHostParam: *missingHostParam, + FilePath: *filePath, + CustomHostParam: *customHostParam, + CustomHostParamSet: customHostParamSet, + } + switch *client { + case CURL: + curlShellCommand, err := s3Command.CurlShellCommand() + if err != nil { + log.Fatalf("Error generating curl command: %v", err) + } + fmt.Println(curlShellCommand) + case OPENSSL: + if err := s3Command.OpenSSLCommand(); err != nil { + log.Fatalf("Error generating and writing openssl command: %v", err) + } + default: + log.Fatalln("Invalid client type: ", *client) + } + +} + +func checkFlags() error { + method = flag.String("method", "GET", "HTTP method to use") + url = flag.String("url", "https://localhost:7070", "S3 server URL") + bucketName = flag.String("bucketName", "", "Bucket name") + objectKey = flag.String("objectKey", "", "Object key") + query = flag.String("query", "", "S3 query") + awsAccessKeyId = flag.String("awsAccessKeyId", "", "AWS access key ID") + awsSecretAccessKey = flag.String("awsSecretAccessKey", "", "AWS secret access key") + awsRegion = flag.String("awsRegion", "us-east-1", "AWS region") + serviceName = flag.String("serviceName", "s3", "Service name") + logger.Debug = flag.Bool("debug", false, "Print debug statements") + flag.Var(&signedParamsMap, "signedParams", "Signed params, separated by comma") + payloadFile = flag.String("payloadFile", "", "Payload file path, if any") + incorrectSignature = flag.Bool("incorrectSignature", false, "Simulate an incorrect signature") + incorrectYearMonthDay = flag.Bool("incorrectYearMonthDay", false, "Simulate an incorrect year/month/day") + invalidYearMonthDay = flag.Bool("invalidYearMonthDay", false, "Simulate an invalid year/month/day") + incorrectCredential = flag.String("incorrectCredential", "", "Add an incorrect credential string") + authorizationScheme = flag.String("authorizationScheme", "AWS4-HMAC-SHA256", "Authorization Scheme") + payload = flag.String("payload", "", "Message payload") + contentMD5 = flag.Bool("contentMD5", false, "Include content-md5 hash") + incorrectContentMD5 = flag.Bool("incorrectContentMD5", false, "Include incorrect content-md5 hash") + missingHostParam = flag.Bool("missingHostParam", false, "Missing host parameter") + customHostParam = flag.String("customHostParam", "", "Custom host parameter") + filePath = flag.String("filePath", "", "Path to write command (stdout if none)") + client = flag.String("client", CURL, "Command-line client to use") + // Parse the flags + flag.Parse() + + flag.Visit(func(f *flag.Flag) { + if f.Name == "customHostParam" { + customHostParamSet = true + } + }) + + if flag.Lookup("awsAccessKeyId").Value.String() == "" { + return fmt.Errorf("the 'awsAccessKeyId' value must be set") + } + if flag.Lookup("awsSecretAccessKey").Value.String() == "" { + return fmt.Errorf("the 'awsSecretAccessKey' value must be set") + } + return nil +} diff --git a/tests/rest_scripts/logger/logging.go b/tests/rest_scripts/logger/logging.go new file mode 100644 index 0000000..e50c1e0 --- /dev/null +++ b/tests/rest_scripts/logger/logging.go @@ -0,0 +1,11 @@ +package logger + +import "log" + +var Debug *bool + +func PrintDebug(format string, args ...any) { + if *Debug { + log.Printf(format, args...) + } +} diff --git a/tests/rest_scripts/put_bucket_tagging.go b/tests/rest_scripts/put_bucket_tagging.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/tests/rest_scripts/put_bucket_tagging.go @@ -0,0 +1 @@ +package main diff --git a/tests/test_rest_bucket.sh b/tests/test_rest_bucket.sh index 2c76094..de47468 100755 --- a/tests/test_rest_bucket.sh +++ b/tests/test_rest_bucket.sh @@ -23,6 +23,7 @@ source ./tests/commands/list_buckets.sh source ./tests/drivers/create_bucket/create_bucket_rest.sh source ./tests/drivers/get_bucket_ownership_controls/get_bucket_ownership_controls_rest.sh source ./tests/drivers/list_buckets/list_buckets_rest.sh +source ./tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh source ./tests/logger.sh source ./tests/setup.sh source ./tests/util/util_bucket.sh @@ -178,3 +179,46 @@ export RUN_USERS=true run delete_object_empty_bucket_check_error assert_success } + +@test "REST - PutBucketTagging - no payload" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/1521" + fi + run setup_bucket "$BUCKET_ONE_NAME" + assert_success + + run send_rest_go_command_expect_error "400" "InvalidRequest" "Missing required header" "-bucketName" "$BUCKET_ONE_NAME" "-query" "tagging=" "-method" "PUT" + assert_success +} + +@test "REST - PutBucketTagging - invalid Content-MD5" { + run setup_bucket "$BUCKET_ONE_NAME" + assert_success + + run send_rest_go_command_expect_error "400" "InvalidDigest" "you specified" "-bucketName" "$BUCKET_ONE_NAME" "-query" "tagging=" "-method" "PUT" "-signedParams" "Content-MD5:dummy" \ + "-payload" "keyvalue" + assert_success +} + +@test "REST - PutBucketTagging - invalid Content-MD5 - invalid Content-MD5 itself returned" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/1526" + fi + run setup_bucket "$BUCKET_ONE_NAME" + assert_success + + run send_put_bucket_tagging_command_check_invalid_content_md5 "$BUCKET_ONE_NAME" + assert_success +} + +@test "REST - PutBucketTagging - incorrect Content-MD5" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/1525" + fi + run setup_bucket "$BUCKET_ONE_NAME" + assert_success + + run send_rest_go_command_expect_error "400" "BadDigest" "did not match" "-bucketName" "$BUCKET_ONE_NAME" "-query" "tagging=" "-method" "PUT" "-incorrectContentMD5" \ + "-payload" "keyvalue" + assert_success +} diff --git a/tests/test_rest_list_buckets.sh b/tests/test_rest_list_buckets.sh index c9e226b..b401e2f 100755 --- a/tests/test_rest_list_buckets.sh +++ b/tests/test_rest_list_buckets.sh @@ -20,6 +20,7 @@ load ./bats-assert/load source ./tests/commands/list_buckets.sh source ./tests/drivers/list_buckets/list_buckets_rest.sh source ./tests/drivers/user.sh +source ./tests/util/util_setup.sh source ./tests/logger.sh source ./tests/setup.sh @@ -107,6 +108,14 @@ export RUN_USERS=true assert_success } +@test "REST - missing host parameter" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/1530" + fi + run send_openssl_go_command "400" "-missingHostParam" + assert_success +} + @test "test_rest_list_buckets" { run setup_bucket "$BUCKET_ONE_NAME" assert_success @@ -170,4 +179,3 @@ export RUN_USERS=true run list_check_buckets_user "$username" "$password" "$BUCKET_TWO_NAME" assert_success } -