mirror of
https://github.com/versity/versitygw.git
synced 2025-12-23 05:05:16 +00:00
test: more list-buckets, bucket tagging tests, dockerfile enhancements
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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="<param>=<amd library or folder>"` 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 <tag> -f Dockerfile_test_bats --build-arg="SECRETS_FILE=<file>" --build-arg="CONFIG_FILE=<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 <tag> --build-arg="SECRETS_FILE=<file>" --build-arg="CONFIG_FILE=<file>" -f tests/Dockerfile_test_bats .`.
|
||||
4. To run the entire suite, run `docker run -it <image name>`. 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 <image name> rest`). Also, multiple specific suites can be run, if separated by comma.
|
||||
|
||||
## Instructions - Running with docker-compose
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
44
tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh
Normal file
44
tests/drivers/put_bucket_tagging/put_bucket_tagging_rest.sh
Normal file
@@ -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" "<Tagging xmlms=\\\"http://s3.amazonaws.com/doc/2006-03-01/\\\"><TagSet><Tag><Key>key</Key><Value>value</Value></Tag></TagSet></Tagging>"; 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
152
tests/rest_scripts/generateCommand.go
Normal file
152
tests/rest_scripts/generateCommand.go
Normal file
@@ -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
|
||||
}
|
||||
11
tests/rest_scripts/logger/logging.go
Normal file
11
tests/rest_scripts/logger/logging.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package logger
|
||||
|
||||
import "log"
|
||||
|
||||
var Debug *bool
|
||||
|
||||
func PrintDebug(format string, args ...any) {
|
||||
if *Debug {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
1
tests/rest_scripts/put_bucket_tagging.go
Normal file
1
tests/rest_scripts/put_bucket_tagging.go
Normal file
@@ -0,0 +1 @@
|
||||
package main
|
||||
@@ -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" "<Tagging xmlms=\\\"http://s3.amazonaws.com/doc/2006-03-01/\\\"><TagSet><Tag><Key>key</Key><Value>value</Value></Tag></TagSet></Tagging>"
|
||||
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" "<Tagging xmlms=\\\"http://s3.amazonaws.com/doc/2006-03-01/\\\"><TagSet><Tag><Key>key</Key><Value>value</Value></Tag></TagSet></Tagging>"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user