mirror of
https://github.com/versity/versitygw.git
synced 2026-07-02 16:54:25 +00:00
27f04ad5ea
This change adds Windows functional test execution in CI and updates backend handling so windows filesystem error/path semantics map correctly to expected S3 outcomes. The only meta supported on windows right now is sidecar, so the tests in windows mode also skip sidecar skips. Future work is to address the skips and/or more clearly document the unsupported/incompatible behavior on windows. The windows support will still remain best effort, but these tests should at least flag when future changes introduce incompatible behavior on windows.
965 lines
26 KiB
Go
965 lines
26 KiB
Go
// Copyright 2023 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.
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/versity/versitygw/s3err"
|
|
)
|
|
|
|
func PresignedAuth_security_token_not_supported(s *S3Conf) error {
|
|
testName := "PresignedAuth_security_token_not_supported"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri := v4req.URL + "&X-Amz-Security-Token=my_token"
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.SecurityTokenNotSupported())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_unsupported_algorithm(s *S3Conf) error {
|
|
testName := "PresignedAuth_unsupported_algorithm"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri := strings.Replace(v4req.URL, "AWS4-HMAC-SHA256", "AWS4-SHA256", 1)
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.UnsupportedAlgorithm())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_ECDSA_not_supported(s *S3Conf) error {
|
|
testName := "PresignedAuth_ECDSA_not_supported"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri := strings.Replace(v4req.URL, "AWS4-HMAC-SHA256", "AWS4-ECDSA-P256-SHA256", 1)
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.OnlyHMACSupported())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_missing_signature_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_missing_signature_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Del("X-Amz-Signature")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_missing_credentials_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_missing_credentials_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Del("X-Amz-Credential")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_malformed_creds_invalid_parts(s *S3Conf) error {
|
|
testName := "PresignedAuth_malformed_creds_invalid_parts"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Set("X-Amz-Credential", "access/hello/world")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MalformedCredential(""))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_creds_invalid_terminal(s *S3Conf) error {
|
|
testName := "PresignedAuth_creds_invalid_terminal"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri, err := changeAuthCred(v4req.URL, "aws5_request", credTerminator)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectTerminal("", "aws5_request"))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_creds_incorrect_service(s *S3Conf) error {
|
|
testName := "PresignedAuth_creds_incorrect_service"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri, err := changeAuthCred(v4req.URL, "sns", credService)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectService("", "sns"))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_creds_incorrect_region(s *S3Conf) error {
|
|
testName := "PresignedAuth_creds_incorrect_region"
|
|
return presignedAuthHandler(s, testName, func(_ *s3.PresignClient, bucket string) error {
|
|
cfg := *s
|
|
|
|
if cfg.awsRegion == "us-east-1" {
|
|
cfg.awsRegion = "us-west-1"
|
|
} else {
|
|
cfg.awsRegion = "us-east-1"
|
|
}
|
|
|
|
client := cfg.GetPresignClient()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectRegion(s.awsRegion, cfg.awsRegion))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_creds_invalid_date(s *S3Conf) error {
|
|
testName := "PresignedAuth_creds_invalid_date"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri, err := changeAuthCred(v4req.URL, "32234Z34", credDate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.InvalidDateFormat("", "32234Z34"))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_non_existing_access_key_id(s *S3Conf) error {
|
|
testName := "PresignedAuth_non_existing_access_key_id"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
accessKeyID := "a_rarely_existing_access_key_id890asd6f807as6ydf870say"
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri, err := changeAuthCred(v4req.URL, accessKeyID, credAccess)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.GetInvalidAccessKeyIdErr(accessKeyID))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_missing_date_query(s *S3Conf) error {
|
|
testName := "PresignedAuth_missing_date_query"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Del("X-Amz-Date")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_dates_mismatch(s *S3Conf) error {
|
|
testName := "PresignedAuth_dates_mismatch"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tDate := urlParsed.Query().Get("X-Amz-Date")
|
|
date := tDate[:8]
|
|
|
|
uri, err := changeAuthCred(v4req.URL, "20060102", credDate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.DateMismatch("20060102", date))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_missing_signed_headers_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_missing_signed_headers_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Del("X-Amz-SignedHeaders")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_unsigned_required_header(s *S3Conf) error {
|
|
testName := "PresignedAuth_unsigned_required_header"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: getPtr("my-obj")})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("X-Amz-Copy-Source", "source-bucket/source-key")
|
|
req.Header.Set("X-Amz-Tagging", "a=b")
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.GetHeadersNotSignedErr([]string{"x-amz-copy-source", "x-amz-tagging"}))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_unsigned_non_required_header(s *S3Conf) error {
|
|
testName := "PresignedAuth_unsigned_non_required_header"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: getPtr("my-obj")})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "text/plain")
|
|
req.Header.Set("X-Custom-Header", "value")
|
|
req.Header.Set("X-Another-Custom-Header", "value")
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected response status code to be %v, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_missing_expiration_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_missing_expiration_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Del("X-Amz-Expires")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_invalid_expiration_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_invalid_expiration_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Set("X-Amz-Expires", "invalid_value")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresNumber())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_negative_expiration_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_negative_expiration_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Set("X-Amz-Expires", "-3")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresNegative())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_exceeding_expiration_query_param(s *S3Conf) error {
|
|
testName := "PresignedAuth_exceeding_expiration_query_param"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Set("X-Amz-Expires", "60580000")
|
|
urlParsed.RawQuery = queries.Encode()
|
|
|
|
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresTooLarge())
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_expired_request(s *S3Conf) error {
|
|
testName := "PresignedAuth_expired_request"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
urlParsed, err := url.Parse(v4req.URL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
expDate := time.Now().AddDate(0, -1, 0).Format(iso8601Format)
|
|
|
|
queries := urlParsed.Query()
|
|
queries.Set("X-Amz-Date", expDate)
|
|
urlParsed.RawQuery = queries.Encode()
|
|
xAmzExpires, err := strconv.Atoi(queries.Get("X-Amz-Expires"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri, err := changeAuthCred(urlParsed.String(), expDate[:8], credDate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := checkHTTPResponseApiErr(resp, s3err.GetExpiredPresignedURLError(xAmzExpires, "", "")); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_incorrect_secret_key(s *S3Conf) error {
|
|
testName := "PresignedAuth_incorrect_secret_key"
|
|
return presignedAuthHandler(s, testName, func(_ *s3.PresignClient, bucket string) error {
|
|
cfg := *s
|
|
cfg.awsSecret += "x"
|
|
client := cfg.GetPresignClient()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_sigv2_not_supported(s *S3Conf) error {
|
|
testName := "PresignedAuth_sigv2_not_supported"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
expires := time.Now().UTC().Add(time.Hour).Unix()
|
|
uri := fmt.Sprintf("%s/%s/object?AWSAccessKeyId=%s&Expires=%d&Signature=my-signature", s.endpoint, bucket, s.awsID, expires)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
|
|
if err != nil {
|
|
cancel()
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationMechanism))
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_PutObject_success(s *S3Conf) error {
|
|
testName := "PresignedAuth_PutObject_success"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: getPtr("my-obj")})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPut, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected my-obj to be successfully uploaded and get 200 response status, instead got %v", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_Put_GetObject_with_data(s *S3Conf) error {
|
|
testName := "PresignedAuth_Put_GetObject_with_data"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
obj := "my-obj"
|
|
|
|
data := "Hello world"
|
|
body := strings.NewReader(data)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &obj, Body: body})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header = v4req.SignedHeader
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected my-obj to be successfully uploaded and get %v response status, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
v4GetReq, err := client.PresignGetObject(ctx, &s3.GetObjectInput{Bucket: &bucket, Key: &obj})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err = http.NewRequest(v4GetReq.Method, v4GetReq.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err = s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected get object response status to be %v, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("read get object response body %w", err)
|
|
}
|
|
|
|
if string(respBody) != data {
|
|
return fmt.Errorf("expected get object response body to be %v, instead got %s", data, respBody)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_Put_GetObject_with_UTF8_chars(s *S3Conf) error {
|
|
testName := "PresignedAuth_Put_GetObject_with_UTF8_chars"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
obj := "my-$%^&*;"
|
|
if s.windowsTests {
|
|
// '*' is not a valid filename character on Windows
|
|
obj = "my-$%^&;"
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &obj})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header = v4req.SignedHeader
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected my-obj to be successfully uploaded and get %v response status, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
v4GetReq, err := client.PresignGetObject(ctx, &s3.GetObjectInput{Bucket: &bucket, Key: &obj})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err = http.NewRequest(v4GetReq.Method, v4GetReq.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err = s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected get object response status to be %v, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func PresignedAuth_UploadPart(s *S3Conf) error {
|
|
testName := "PresignedAuth_UploadPart"
|
|
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
|
key, partNumber := "my-mp", int32(1)
|
|
|
|
clt := s.GetClient()
|
|
mp, err := createMp(clt, bucket, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
v4req, err := client.PresignUploadPart(ctx, &s3.UploadPartInput{Bucket: &bucket, Key: &key, UploadId: mp.UploadId, PartNumber: &partNumber})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("expected response status code to be %v, instead got %v", http.StatusOK, resp.StatusCode)
|
|
}
|
|
|
|
etag := resp.Header.Get("Etag")
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
out, err := clt.ListParts(ctx, &s3.ListPartsInput{Bucket: &bucket, Key: &key, UploadId: mp.UploadId})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(out.Parts) != 1 {
|
|
return fmt.Errorf("expected mp upload parts length to be 1, instead got %v", len(out.Parts))
|
|
}
|
|
if getString(out.Parts[0].ETag) != etag {
|
|
return fmt.Errorf("expected uploaded part etag to be %v, instead got %v", etag, getString(out.Parts[0].ETag))
|
|
}
|
|
if out.Parts[0].PartNumber == nil {
|
|
return fmt.Errorf("expected uploaded part part-number to be not nil")
|
|
}
|
|
if *out.Parts[0].PartNumber != partNumber {
|
|
return fmt.Errorf("expected uploaded part part-number to be %v, instead got %v", partNumber, *out.Parts[0].PartNumber)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|