diff --git a/aws/aws.go b/aws/aws.go index 4ee177c3be..a62c08fe1d 100644 --- a/aws/aws.go +++ b/aws/aws.go @@ -195,6 +195,7 @@ func NewDefaultV2Config(ctx context.Context) (awsv2.Config, error) { // // The following query options are supported: // - region: The AWS region for requests; sets WithRegion. +// - anonymous: A value of "true" forces use of anonymous credentials. // - profile: The shared config profile to use; sets SharedConfigProfile. // - endpoint: The AWS service endpoint to send HTTP request. // - hostname_immutable: Make the hostname immutable, only works if endpoint is also set. @@ -245,6 +246,14 @@ func V2ConfigFromURLParams(ctx context.Context, q url.Values) (awsv2.Config, err if err != nil { return awsv2.Config{}, fmt.Errorf("invalid value for capacity: %w", err) } + case "anonymous": + anon, err := strconv.ParseBool(value) + if err != nil { + return awsv2.Config{}, fmt.Errorf("invalid value for anonymous: %w", err) + } + if anon { + opts = append(opts, awsv2cfg.WithCredentialsProvider(awsv2.AnonymousCredentials{})) + } case "awssdk": // ignore, should be handled before this default: diff --git a/aws/aws_test.go b/aws/aws_test.go index e48e16aac0..e1780689ea 100644 --- a/aws/aws_test.go +++ b/aws/aws_test.go @@ -199,6 +199,10 @@ func TestV2ConfigFromURLParams(t *testing.T) { name: "FIPS and dual stack", query: url.Values{"fips": {"true"}, "dualstack": {"true"}}, }, + { + name: "anonymous", + query: url.Values{"anonymous": {"true"}}, + }, { name: "Rate limit capacity", query: url.Values{"rate_limiter_capacity": {"500"}}, diff --git a/blob/gcsblob/gcsblob.go b/blob/gcsblob/gcsblob.go index 55e3091bed..acb3a0da1c 100644 --- a/blob/gcsblob/gcsblob.go +++ b/blob/gcsblob/gcsblob.go @@ -67,6 +67,7 @@ import ( "net/url" "os" "sort" + "strconv" "strings" "sync" "time" @@ -206,14 +207,13 @@ const Scheme = "gs" // // The following query parameters are supported: // -// - access_id: sets Options.GoogleAccessID -// - private_key_path: path to read for Options.PrivateKey -// -// Currently their use is limited to SignedURL, except that setting access_id -// to "-" forces the use of an unauthenticated client. +// - anonymous: A value of "true" forces the use of an unauthenticated client. +// - access_id: Sets Options.GoogleAccessID; only used in SignedURL, except that +// a value of "-" forces the use of an unauthenticated client. +// - private_key_path: Path to read for Options.PrivateKey; only used in SignedURL. type URLOpener struct { // Client must be set to a non-nil HTTP client authenticated with - // Cloud Storage scope or equivalent. + // Cloud Storage scope or equivalent (unless anonymous=true). Client *gcp.HTTPClient // Options specifies the default options to pass to OpenBucket. @@ -231,13 +231,23 @@ func (o *URLOpener) OpenBucketURL(ctx context.Context, u *url.URL) (*blob.Bucket func (o *URLOpener) forParams(ctx context.Context, q url.Values) (*Options, *gcp.HTTPClient, error) { for k := range q { - if k != "access_id" && k != "private_key_path" { + if k != "access_id" && k != "private_key_path" && k != "anonymous" { return nil, nil, fmt.Errorf("invalid query parameter %q", k) } } opts := new(Options) *opts = o.Options client := o.Client + if anon := q.Get("anonymous"); anon != "" { + isAnon, err := strconv.ParseBool(anon) + if err != nil { + return nil, nil, fmt.Errorf("invalid value %q for query parameter \"anonymous\": %w", anon, err) + } + if isAnon { + opts.clear() + client = gcp.NewAnonymousHTTPClient(gcp.DefaultTransport()) + } + } if accessID := q.Get("access_id"); accessID != "" && accessID != opts.GoogleAccessID { opts.clear() if accessID == "-" { diff --git a/blob/gcsblob/gcsblob_test.go b/blob/gcsblob/gcsblob_test.go index 7d49fc477b..6a3dca810b 100644 --- a/blob/gcsblob/gcsblob_test.go +++ b/blob/gcsblob/gcsblob_test.go @@ -577,6 +577,22 @@ func TestURLOpenerForParams(t *testing.T) { currOpts: Options{GoogleAccessID: "bar"}, wantOpts: Options{GoogleAccessID: "bar"}, }, + { + name: "Invalid value for anonymous", + query: url.Values{ + "anonymous": {"bad"}, + }, + wantErr: true, + }, + { + name: "Anonymous", + currOpts: Options{GoogleAccessID: "foo"}, + query: url.Values{ + "anonymous": {"true"}, + }, + wantOpts: Options{}, // cleared + wantClient: true, + }, { name: "BadPrivateKeyPath", query: url.Values{ diff --git a/blob/s3blob/s3blob_test.go b/blob/s3blob/s3blob_test.go index 508964ab25..d0e0573f77 100644 --- a/blob/s3blob/s3blob_test.go +++ b/blob/s3blob/s3blob_test.go @@ -490,6 +490,8 @@ func TestOpenBucketFromURL(t *testing.T) { {"s3://mybucket?disable_https=true", false}, // OK, use FIPS endpoints (v1) {"s3://mybucket?awssdk=v1&fips=true", false}, + // OK, use anonymous. + {"s3://mybucket?awssdk=v2&anonymous=true", false}, // Invalid accelerate (v1) {"s3://mybucket?awssdk=v1&accelerate=bogus", true}, // Invalid accelerate (v2)