diff --git a/weed/s3api/auto_signature_v4_test.go b/weed/s3api/auto_signature_v4_test.go index cd2e4df15..e0c9aff54 100644 --- a/weed/s3api/auto_signature_v4_test.go +++ b/weed/s3api/auto_signature_v4_test.go @@ -6,13 +6,10 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" - "errors" "fmt" "io" "net/http" - "net/url" "sort" - "strconv" "strings" "sync" "testing" @@ -210,6 +207,53 @@ func mustNewPresignedRequest(iam *IdentityAccessManagement, method string, urlSt return req } +// preSignV4 adds presigned URL parameters to the request +func preSignV4(iam *IdentityAccessManagement, req *http.Request, accessKey, secretKey string, expires int64) error { + // Create credential scope + now := time.Now().UTC() + dateStr := now.Format(iso8601Format) + + // Create credential header + scope := fmt.Sprintf("%s/%s/%s/%s", now.Format(yyyymmdd), "us-east-1", "s3", "aws4_request") + credential := fmt.Sprintf("%s/%s", accessKey, scope) + + // Get the query parameters + query := req.URL.Query() + query.Set("X-Amz-Algorithm", signV4Algorithm) + query.Set("X-Amz-Credential", credential) + query.Set("X-Amz-Date", dateStr) + query.Set("X-Amz-Expires", fmt.Sprintf("%d", expires)) + query.Set("X-Amz-SignedHeaders", "host") + + // Set the query on the URL (without signature yet) + req.URL.RawQuery = query.Encode() + + // Get the payload hash + hashedPayload := getContentSha256Cksum(req) + + // Extract signed headers + extractedSignedHeaders := make(http.Header) + extractedSignedHeaders["host"] = []string{req.Host} + + // Get canonical request + canonicalRequest := getCanonicalRequest(extractedSignedHeaders, hashedPayload, req.URL.RawQuery, req.URL.Path, req.Method) + + // Get string to sign + stringToSign := getStringToSign(canonicalRequest, now, scope) + + // Get signing key + signingKey := getSigningKey(secretKey, now.Format(yyyymmdd), "us-east-1", "s3") + + // Calculate signature + signature := getSignature(signingKey, stringToSign) + + // Add signature to query + query.Set("X-Amz-Signature", signature) + req.URL.RawQuery = query.Encode() + + return nil +} + // Returns new HTTP request object. func newTestRequest(method, urlStr string, contentLength int64, body io.ReadSeeker) (*http.Request, error) { if method == "" { @@ -459,47 +503,6 @@ func signRequestV4(req *http.Request, accessKey, secretKey string) error { return nil } -// preSignV4 presign the request, in accordance with -// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html. -func preSignV4(iam *IdentityAccessManagement, req *http.Request, accessKeyID, secretAccessKey string, expires int64) error { - // Presign is not needed for anonymous credentials. - if accessKeyID == "" || secretAccessKey == "" { - return errors.New("Presign cannot be generated without access and secret keys") - } - - region := "us-east-1" - date := time.Now().UTC() - scope := getScope(date, region) - credential := fmt.Sprintf("%s/%s", accessKeyID, scope) - - // Set URL query. - query := req.URL.Query() - query.Set("X-Amz-Algorithm", signV4Algorithm) - query.Set("X-Amz-Date", date.Format(iso8601Format)) - query.Set("X-Amz-Expires", strconv.FormatInt(expires, 10)) - query.Set("X-Amz-SignedHeaders", "host") - query.Set("X-Amz-Credential", credential) - query.Set("X-Amz-Content-Sha256", unsignedPayload) - - // "host" is the only header required to be signed for Presigned URLs. - extractedSignedHeaders := make(http.Header) - extractedSignedHeaders.Set("host", req.Host) - - queryStr := strings.Replace(query.Encode(), "+", "%20", -1) - canonicalRequest := getCanonicalRequest(extractedSignedHeaders, unsignedPayload, queryStr, req.URL.Path, req.Method) - stringToSign := getStringToSign(canonicalRequest, date, scope) - signingKey := getSigningKey(secretAccessKey, date.Format(yyyymmdd), region, "s3") - signature := getSignature(signingKey, stringToSign) - - req.URL.RawQuery = query.Encode() - - // Add signature header to RawQuery. - req.URL.RawQuery += "&X-Amz-Signature=" + url.QueryEscape(signature) - - // Construct the final presigned URL. - return nil -} - // EncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences // // This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8