Merge pull request #28 from nemunaire/b/signedurl_for_custom_endpoint

Rework presign function to allow signature of clients with dedicated endpoint
This commit is contained in:
Rohan Verma
2025-04-23 23:25:19 +05:30
committed by GitHub
2 changed files with 28 additions and 22 deletions
+24 -18
View File
@@ -5,6 +5,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"net/url" "net/url"
"path"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -24,8 +25,6 @@ type PresignedInput struct {
Timestamp time.Time Timestamp time.Time
ExtraHeaders map[string]string ExtraHeaders map[string]string
ExpirySeconds int ExpirySeconds int
Protocol string
Endpoint string
} }
// GeneratePresignedURL creates a Presigned URL that can be used // GeneratePresignedURL creates a Presigned URL that can be used
@@ -35,8 +34,9 @@ func (s3 *S3) GeneratePresignedURL(in PresignedInput) string {
var ( var (
nowTime = nowTime() nowTime = nowTime()
protocol = defaultProtocol protocol = defaultProtocol
endpoint = defaultPresignedHost hostname = defaultPresignedHost
path_prefix = ""
) )
if !in.Timestamp.IsZero() { if !in.Timestamp.IsZero() {
nowTime = in.Timestamp.UTC() nowTime = in.Timestamp.UTC()
@@ -52,11 +52,16 @@ func (s3 *S3) GeneratePresignedURL(in PresignedInput) string {
b.Reset() b.Reset()
// Set the protocol as default if not provided. // Set the protocol as default if not provided.
if in.Protocol != "" { if endpoint, _ := url.Parse(s3.Endpoint); endpoint.Host != "" {
protocol = in.Protocol protocol = endpoint.Scheme + "://"
} hostname = endpoint.Host
if in.Endpoint != "" { path_prefix = path.Join("/", endpoint.Path, in.Bucket)
endpoint = in.Endpoint } else {
host := bytes.Buffer{}
host.WriteString(in.Bucket)
host.WriteRune('.')
host.WriteString(hostname)
hostname = host.String()
} }
// Add host to Headers // Add host to Headers
@@ -64,16 +69,13 @@ func (s3 *S3) GeneratePresignedURL(in PresignedInput) string {
for k, v := range in.ExtraHeaders { for k, v := range in.ExtraHeaders {
signedHeaders[k] = []byte(v) signedHeaders[k] = []byte(v)
} }
host := bytes.Buffer{} signedHeaders["host"] = []byte(hostname)
host.WriteString(in.Bucket)
host.WriteRune('.')
host.WriteString(endpoint)
signedHeaders["host"] = host.Bytes()
// Start Canonical Request Formation // Start Canonical Request Formation
h := sha256.New() // We write the canonical request directly to the SHA256 hash. h := sha256.New() // We write the canonical request directly to the SHA256 hash.
h.Write([]byte(in.Method)) // HTTP Verb h.Write([]byte(in.Method)) // HTTP Verb
h.Write(newLine) h.Write(newLine)
h.Write([]byte(path_prefix))
h.Write([]byte{'/'}) h.Write([]byte{'/'})
h.Write([]byte(in.ObjectKey)) // CanonicalURL h.Write([]byte(in.ObjectKey)) // CanonicalURL
h.Write(newLine) h.Write(newLine)
@@ -193,10 +195,14 @@ func (s3 *S3) GeneratePresignedURL(in PresignedInput) string {
b.Reset() b.Reset()
// Start Generating URL // Start Generating URL
b.WriteString(protocol) if s3.Endpoint != "" {
b.WriteString(in.Bucket) b.WriteString(s3.Endpoint)
b.WriteRune('.') b.WriteRune('/')
b.WriteString(endpoint) b.WriteString(in.Bucket)
} else {
b.WriteString(protocol)
b.WriteString(hostname)
}
b.WriteRune('/') b.WriteRune('/')
b.WriteString(in.ObjectKey) b.WriteString(in.ObjectKey)
b.WriteRune('?') b.WriteRune('?')
+4 -4
View File
@@ -60,10 +60,10 @@ func TestS3_GeneratePresignedURL_Personal(t *testing.T) {
os.Getenv("AWS_S3_ACCESS_KEY"), os.Getenv("AWS_S3_ACCESS_KEY"),
os.Getenv("AWS_S3_SECRET_KEY"), os.Getenv("AWS_S3_SECRET_KEY"),
) )
s.Endpoint = os.Getenv("AWS_S3_ENDPOINT")
dontwant := "" dontwant := ""
if got := s.GeneratePresignedURL(PresignedInput{ if got := s.GeneratePresignedURL(PresignedInput{
Bucket: os.Getenv("AWS_S3_BUCKET"), Bucket: os.Getenv("AWS_S3_BUCKET"),
Endpoint: os.Getenv("AWS_S3_ENDPOINT"),
ObjectKey: "test1.txt", ObjectKey: "test1.txt",
Method: "GET", Method: "GET",
Timestamp: nowTime(), Timestamp: nowTime(),
@@ -81,10 +81,10 @@ func TestS3_GeneratePresignedURL_ExtraHeader(t *testing.T) {
os.Getenv("AWS_S3_ACCESS_KEY"), os.Getenv("AWS_S3_ACCESS_KEY"),
os.Getenv("AWS_S3_SECRET_KEY"), os.Getenv("AWS_S3_SECRET_KEY"),
) )
s.Endpoint = os.Getenv("AWS_S3_ENDPOINT")
dontwant := "" dontwant := ""
if got := s.GeneratePresignedURL(PresignedInput{ if got := s.GeneratePresignedURL(PresignedInput{
Bucket: os.Getenv("AWS_S3_BUCKET"), Bucket: os.Getenv("AWS_S3_BUCKET"),
Endpoint: os.Getenv("AWS_S3_ENDPOINT"),
ObjectKey: "test2.txt", ObjectKey: "test2.txt",
Method: "GET", Method: "GET",
Timestamp: nowTime(), Timestamp: nowTime(),
@@ -105,10 +105,10 @@ func TestS3_GeneratePresignedURL_PUT(t *testing.T) {
os.Getenv("AWS_S3_ACCESS_KEY"), os.Getenv("AWS_S3_ACCESS_KEY"),
os.Getenv("AWS_S3_SECRET_KEY"), os.Getenv("AWS_S3_SECRET_KEY"),
) )
s.Endpoint = os.Getenv("AWS_S3_ENDPOINT")
dontwant := "" dontwant := ""
if got := s.GeneratePresignedURL(PresignedInput{ if got := s.GeneratePresignedURL(PresignedInput{
Bucket: os.Getenv("AWS_S3_BUCKET"), Bucket: os.Getenv("AWS_S3_BUCKET"),
Endpoint: os.Getenv("AWS_S3_ENDPOINT"),
ObjectKey: "test2.txt", ObjectKey: "test2.txt",
Method: "PUT", Method: "PUT",
Timestamp: nowTime(), Timestamp: nowTime(),
@@ -126,13 +126,13 @@ func BenchmarkS3_GeneratePresigned(b *testing.B) {
os.Getenv("AWS_S3_ACCESS_KEY"), os.Getenv("AWS_S3_ACCESS_KEY"),
os.Getenv("AWS_S3_SECRET_KEY"), os.Getenv("AWS_S3_SECRET_KEY"),
) )
s.Endpoint = os.Getenv("AWS_S3_ENDPOINT")
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
s.GeneratePresignedURL(PresignedInput{ s.GeneratePresignedURL(PresignedInput{
Bucket: os.Getenv("AWS_S3_BUCKET"), Bucket: os.Getenv("AWS_S3_BUCKET"),
Endpoint: os.Getenv("AWS_S3_ENDPOINT"),
ObjectKey: "test.txt", ObjectKey: "test.txt",
Method: "GET", Method: "GET",
Timestamp: nowTime(), Timestamp: nowTime(),