From 8d9f52f58a4553daa97f38729e9dc11dbe0aad80 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Fri, 16 Sep 2022 16:52:55 -0700 Subject: [PATCH 01/22] Adding UDK/UDC to service client --- sdk/storage/azblob/common.go | 17 +++ sdk/storage/azblob/constants.go | 3 + sdk/storage/azblob/internal/base/clients.go | 29 +++- .../azblob/internal/exported/account_sas.go | 2 +- .../azblob/internal/exported/service_sas.go | 60 ++++----- .../exported/shared_key_credential.go | 10 +- .../exported/storage_account_credential.go | 16 +++ .../exported/user_delegation_credential.go | 47 +++++++ sdk/storage/azblob/service/client.go | 26 ++++ sdk/storage/azblob/service/client_test.go | 127 ++++++++++++++++++ sdk/storage/azblob/service/models.go | 10 ++ sdk/storage/azblob/service/responses.go | 3 + sdk/storage/azblob/zt_examples_test.go | 47 +++++++ 13 files changed, 361 insertions(+), 36 deletions(-) create mode 100644 sdk/storage/azblob/internal/exported/storage_account_credential.go create mode 100644 sdk/storage/azblob/internal/exported/user_delegation_credential.go diff --git a/sdk/storage/azblob/common.go b/sdk/storage/azblob/common.go index c16e96cdc97a..cc038aaf1c14 100644 --- a/sdk/storage/azblob/common.go +++ b/sdk/storage/azblob/common.go @@ -8,6 +8,7 @@ package azblob import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" ) // SharedKeyCredential contains an account's name and its primary or secondary key. @@ -19,6 +20,19 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } +// UserDelegationCredntial contains an account's name and its user delegation key. +type UserDelegationCredential = exported.UserDelegationCredential + +// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it +func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { + return exported.NewUserDelegationCredential(accountName, key) +} + +type UserDelegationKey = generated.UserDelegationKey + +// StorageAccountCredential is a wrapper interface for SharedKeyCredential and UserDelegationCredential +type StorageAccountCredential = exported.StorageAccountCredential + // IPEndpointStyleInfo is used for IP endpoint style URL when working with Azure storage emulator. // Ex: "https://10.132.141.33/accountname/containername" type IPEndpointStyleInfo = exported.IPEndpointStyleInfo @@ -46,3 +60,6 @@ type SASProtocol = exported.SASProtocol // IPRange represents a SAS IP range's start IP and (optionally) end IP. type IPRange = exported.IPRange + +// KeyInfo - Key Information +type KeyInfo = generated.KeyInfo diff --git a/sdk/storage/azblob/constants.go b/sdk/storage/azblob/constants.go index d4c3262d932d..4fe921e17603 100644 --- a/sdk/storage/azblob/constants.go +++ b/sdk/storage/azblob/constants.go @@ -7,6 +7,7 @@ package azblob import ( + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" ) @@ -35,3 +36,5 @@ const ( func PossibleDeleteSnapshotsOptionTypeValues() []DeleteSnapshotsOptionType { return generated.PossibleDeleteSnapshotsOptionTypeValues() } + +const SASTimeFormat = exported.SASTimeFormat // "2017-07-27T00:00:00Z" // ISO 8601 diff --git a/sdk/storage/azblob/internal/base/clients.go b/sdk/storage/azblob/internal/base/clients.go index 16e6cac066db..23cfacc437bc 100644 --- a/sdk/storage/azblob/internal/base/clients.go +++ b/sdk/storage/azblob/internal/base/clients.go @@ -13,8 +13,9 @@ import ( ) type Client[T any] struct { - inner *T - sharedKey *exported.SharedKeyCredential + inner *T + sharedKey *exported.SharedKeyCredential + userDelegationKey *exported.UserDelegationKey } func InnerClient[T any](client *Client[T]) *T { @@ -50,6 +51,30 @@ func NewBlobClient(blobURL string, pipeline runtime.Pipeline, sharedKey *exporte } } +func NewServiceClientUDK(containerURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.ServiceClient] { + return &Client[generated.ServiceClient]{ + inner: generated.NewServiceClient(containerURL, pipeline), + sharedKey: sharedKey, + userDelegationKey: udk, + } +} + +func NewContainerClientUDK(containerURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.ContainerClient] { + return &Client[generated.ContainerClient]{ + inner: generated.NewContainerClient(containerURL, pipeline), + sharedKey: sharedKey, + userDelegationKey: udk, + } +} + +func NewBlobClientUDK(blobURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.BlobClient] { + return &Client[generated.BlobClient]{ + inner: generated.NewBlobClient(blobURL, pipeline), + sharedKey: sharedKey, + userDelegationKey: udk, + } +} + type CompositeClient[T, U any] struct { innerT *T innerU *U diff --git a/sdk/storage/azblob/internal/exported/account_sas.go b/sdk/storage/azblob/internal/exported/account_sas.go index 2c8cb4873492..7af9845bd3ec 100644 --- a/sdk/storage/azblob/internal/exported/account_sas.go +++ b/sdk/storage/azblob/internal/exported/account_sas.go @@ -58,7 +58,7 @@ func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential ""}, // That is right, the account SAS requires a terminating extra newline "\n") - signature, err := sharedKeyCredential.computeHMACSHA256(stringToSign) + signature, err := sharedKeyCredential.ComputeHMACSHA256(stringToSign) if err != nil { return SASQueryParameters{}, err } diff --git a/sdk/storage/azblob/internal/exported/service_sas.go b/sdk/storage/azblob/internal/exported/service_sas.go index c436e22d9cc0..50ec7443bd82 100644 --- a/sdk/storage/azblob/internal/exported/service_sas.go +++ b/sdk/storage/azblob/internal/exported/service_sas.go @@ -47,9 +47,9 @@ func getDirectoryDepth(path string) string { // Sign uses an account's StorageAccountCredential to sign this signature values to produce the proper SAS query parameters. // See: StorageAccountCredential. Compatible with both UserDelegationCredential and SharedKeyCredential -func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { +func (v BlobSASSignatureValues) Sign(credential StorageAccountCredential) (SASQueryParameters, error) { resource := "c" - if sharedKeyCredential == nil { + if credential == nil { return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without Shared Key Credential") } @@ -100,31 +100,29 @@ func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( signedIdentifier := v.Identifier - //udk := sharedKeyCredential.getUDKParams() - // - //if udk != nil { - // udkStart, udkExpiry, _ := FormatTimesForSASSigning(udk.SignedStart, udk.SignedExpiry, time.Time{}) - // //I don't like this answer to combining the functions - // //But because signedIdentifier and the user delegation key strings share a place, this is an _OK_ way to do it. - // signedIdentifier = strings.Join([]string{ - // udk.SignedOID, - // udk.SignedTID, - // udkStart, - // udkExpiry, - // udk.SignedService, - // udk.SignedVersion, - // v.PreauthorizedAgentObjectId, - // v.AgentObjectID, - // v.CorrelationId, - // }, "\n") - //} + udk := credential.getUDKParams() + + if udk != nil { + udkStart, udkExpiry, _ := FormatTimesForSASSigning(*udk.SignedStart, *udk.SignedExpiry, time.Time{}) + signedIdentifier = strings.Join([]string{ + *udk.SignedOID, + *udk.SignedTID, + udkStart, + udkExpiry, + *udk.SignedService, + *udk.SignedVersion, + v.PreauthorizedAgentObjectId, + v.AgentObjectId, + v.CorrelationId, + }, "\n") + } // String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx stringToSign := strings.Join([]string{ v.Permissions, startTime, expiryTime, - getCanonicalName(sharedKeyCredential.AccountName(), v.ContainerName, v.BlobName, v.Directory), + getCanonicalName(credential.AccountName(), v.ContainerName, v.BlobName, v.Directory), signedIdentifier, v.IPRange.String(), string(v.Protocol), @@ -139,7 +137,7 @@ func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( "\n") signature := "" - signature, err := sharedKeyCredential.computeHMACSHA256(stringToSign) + signature, err := credential.ComputeHMACSHA256(stringToSign) if err != nil { return SASQueryParameters{}, err } @@ -170,15 +168,15 @@ func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( signature: signature, } - ////User delegation SAS specific parameters - //if udk != nil { - // p.signedOID = udk.SignedOID - // p.signedTID = udk.SignedTID - // p.signedStart = udk.SignedStart - // p.signedExpiry = udk.SignedExpiry - // p.signedService = udk.SignedService - // p.signedVersion = udk.SignedVersion - //} + //User delegation SAS specific parameters + if udk != nil { + p.signedOID = *udk.SignedOID + p.signedTID = *udk.SignedTID + p.signedStart = *udk.SignedStart + p.signedExpiry = *udk.SignedExpiry + p.signedService = *udk.SignedService + p.signedVersion = *udk.SignedVersion + } return p, nil } diff --git a/sdk/storage/azblob/internal/exported/shared_key_credential.go b/sdk/storage/azblob/internal/exported/shared_key_credential.go index 12f271ef3f8f..ff7cf1e12f27 100644 --- a/sdk/storage/azblob/internal/exported/shared_key_credential.go +++ b/sdk/storage/azblob/internal/exported/shared_key_credential.go @@ -12,6 +12,7 @@ import ( "crypto/sha256" "encoding/base64" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" "net/http" "net/url" "sort" @@ -58,7 +59,7 @@ func (c *SharedKeyCredential) SetAccountKey(accountKey string) error { } // ComputeHMACSHA256 generates a hash signature for an HTTP request or for a SAS. -func (c *SharedKeyCredential) computeHMACSHA256(message string) (string, error) { +func (c *SharedKeyCredential) ComputeHMACSHA256(message string) (string, error) { h := hmac.New(sha256.New, c.accountKey.Load().([]byte)) _, err := h.Write([]byte(message)) return base64.StdEncoding.EncodeToString(h.Sum(nil)), err @@ -178,6 +179,11 @@ func (c *SharedKeyCredential) buildCanonicalizedResource(u *url.URL) (string, er return cr.String(), nil } +// noop function to satisfy StorageAccountCredential interface +func (f *SharedKeyCredential) getUDKParams() *generated.UserDelegationKey { + return nil +} + // the following content isn't actually exported but must live // next to SharedKeyCredential as it uses its unexported methods @@ -197,7 +203,7 @@ func (s *SharedKeyCredPolicy) Do(req *policy.Request) (*http.Response, error) { if err != nil { return nil, err } - signature, err := s.cred.computeHMACSHA256(stringToSign) + signature, err := s.cred.ComputeHMACSHA256(stringToSign) if err != nil { return nil, err } diff --git a/sdk/storage/azblob/internal/exported/storage_account_credential.go b/sdk/storage/azblob/internal/exported/storage_account_credential.go new file mode 100644 index 000000000000..66027f906112 --- /dev/null +++ b/sdk/storage/azblob/internal/exported/storage_account_credential.go @@ -0,0 +1,16 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package exported + +import "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" + +// StorageAccountCredential is a wrapper interface for SharedKeyCredential and UserDelegationCredential +type StorageAccountCredential interface { + AccountName() string + ComputeHMACSHA256(message string) (string, error) + getUDKParams() *generated.UserDelegationKey +} diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go new file mode 100644 index 000000000000..2cf6bcf629ad --- /dev/null +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -0,0 +1,47 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package exported + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" +) + +// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it +func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { + return &UserDelegationCredential{ + accountName: accountName, + accountKey: key, + } +} + +type UserDelegationCredential struct { + accountName string + accountKey generated.UserDelegationKey +} + +// AccountName returns the Storage account's name +func (f *UserDelegationCredential) AccountName() string { + return f.accountName +} + +// ComputeHMAC +func (f *UserDelegationCredential) ComputeHMACSHA256(message string) (string, error) { + bytes, _ := base64.StdEncoding.DecodeString(*f.accountKey.Value) + h := hmac.New(sha256.New, bytes) + _, err := h.Write([]byte(message)) + return base64.StdEncoding.EncodeToString(h.Sum(nil)), err +} + +// Private method to return important parameters for NewSASQueryParameters +func (f *UserDelegationCredential) getUDKParams() *generated.UserDelegationKey { + return &f.accountKey +} + +type UserDelegationKey = generated.UserDelegationKey diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index ef4d7a1af458..3a3b4d2bfc2a 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -81,6 +81,32 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio return NewClientWithNoCredential(parsed.ServiceURL, options) } +// NewClientWithUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. +// OAuth is required for this call, as well as any role that can delegate access to the storage account. +func NewClientWithUserDelegationCredential(serviceURL string, ctx context.Context, info generated.KeyInfo, timeout *int32, requestID *string) (*Client, error) { + url, err := exported.ParseURL(serviceURL) + if err != nil { + return nil, err + } + pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, nil) + sc := generated.NewServiceClient(serviceURL, pl) + opts := GetUserDelegationKeyOptions{ + RequestID: requestID, + Timeout: timeout, + } + udk, err := sc.GetUserDelegationKey(ctx, info, &opts) + if err != nil { + return nil, err + } + + return (*Client)(base.NewServiceClientUDK(url.Host, pl, nil, &udk.UserDelegationKey)), nil +} + +func (s Client) GetUserDelegationKey(ctx context.Context, keyInfo generated.KeyInfo, options *GetUserDelegationKeyOptions) GetUserDelegationKeyResponse { + resp, _ := s.generated().GetUserDelegationKey(ctx, keyInfo, options) + return resp +} + func (s *Client) generated() *generated.ServiceClient { return base.InnerClient((*base.Client[generated.ServiceClient])(s)) } diff --git a/sdk/storage/azblob/service/client_test.go b/sdk/storage/azblob/service/client_test.go index 9cd1907cccc7..24347fb81475 100644 --- a/sdk/storage/azblob/service/client_test.go +++ b/sdk/storage/azblob/service/client_test.go @@ -598,3 +598,130 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() { //_, err = containerClient2.Create(ctx, nil) //_require.Nil(err) } + +/*func (s *ServiceRecordedTestsSuite) TestUDKServiceClient() { + _require := require.New(s.T()) + testName := s.T().Name() + accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") + + clientOptions := azcore.ClientOptions{} + optsClientID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ClientID("7cf7db0d-...")} + cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) + _require.Nil(err) + + svcClient, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &azblob.ClientOptions{}) + + // Set current and past time + currentTime := time.Now().UTC().Add(-10 * time.Second) + pastTime := currentTime.Add(48 * time.Hour) + info := azblob.KeyInfo{ + Start: to.Ptr(currentTime.UTC().Format(azblob.SASTimeFormat)), + Expiry: to.Ptr(pastTime.UTC().Format(azblob.SASTimeFormat)), + } + + serviceClient, err := service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + _require.Nil(err) + + containerName := testcommon.GenerateContainerName(testName) + + resources := service.SASResourceTypes{ + Object: true, + Service: true, + Container: true, + } + permissions := service.SASPermissions{ + Read: true, + Add: true, + Write: true, + Create: true, + Update: true, + Delete: true, + } + services := service.SASServices{ + Blob: true, + } + start := time.Now().Add(-time.Hour) + expiry := start.Add(time.Hour) + + sasUrl, err := serviceClient.GetSASURL(resources, permissions, services, start, expiry) + _require.Nil(err) + + cl, err := service.NewClientWithUserDelegationCredential(sasUrl, nil, info, nil, nil) + _require.Nil(err) + + _, err = cl.CreateContainer(context.Background(), containerName+"002", nil) + _require.Nil(err) + + _, err = cl.DeleteContainer(context.Background(), containerName+"002", nil) + _require.Nil(err) +}*/ + +/*func (s *ServiceUnrecordedTestsSuite) TestUDKContainerClient() { + _require := require.New(s.T()) + testName := s.T().Name() + accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") + accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY") + cred, err := azblob.NewSharedKeyCredential(accountName, accountKey) + _require.Nil(err) + + serviceClient, err := service.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, nil) + _require.Nil(err) + + containerName := testcommon.GenerateContainerName(testName) + containerClient := serviceClient.NewContainerClient(containerName) + + permissions := container.SASPermissions{ + Read: true, + Add: true, + } + start := time.Now().Add(-5 * time.Minute).UTC() + expiry := time.Now().Add(time.Hour) + + sasUrl, err := containerClient.GetSASURL(permissions, start, expiry) + _require.Nil(err) + + containerClient2, err := container.NewClientWithNoCredential(sasUrl, nil) + _require.Nil(err) + + _, err = containerClient2.Create(context.Background(), &container.CreateOptions{Metadata: testcommon.BasicMetadata}) + _require.NotNil(err) + testcommon.ValidateBlobErrorCode(_require, err, bloberror.AuthorizationFailure) +} + +func (s *ServiceUnrecordedTestsSuite) TestUDKContainerClient2() { + _require := require.New(s.T()) + testName := s.T().Name() + accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") + accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY") + cred, err := azblob.NewSharedKeyCredential(accountName, accountKey) + _require.Nil(err) + + serviceClient, err := service.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, nil) + _require.Nil(err) + + containerName := testcommon.GenerateContainerName(testName) + containerClient := serviceClient.NewContainerClient(containerName) + + sasUrlReadAdd, err := containerClient.GetSASURL(container.SASPermissions{Read: true, Add: true}, + time.Now().Add(-5*time.Minute).UTC(), time.Now().Add(time.Hour)) + _require.Nil(err) + _, err = containerClient.Create(context.Background(), &container.CreateOptions{Metadata: testcommon.BasicMetadata}) + _require.Nil(err) + + containerClient1, err := container.NewClientWithNoCredential(sasUrlReadAdd, nil) + _require.Nil(err) + + _, err = containerClient1.GetProperties(context.Background(), nil) + _require.Nil(err) + //validateBlobErrorCode(_require, err, bloberror.AuthorizationFailure) + // + //sasUrlRCWL, err := containerClient.GetSASURL(container.SASPermissions{Add: true, Create: true, Delete: true, List: true}, + // time.Now().Add(-5*time.Minute).UTC(), time.Now().Add(time.Hour)) + //_require.Nil(err) + // + //containerClient2, err := container.NewClientWithNoCredential(sasUrlRCWL, nil) + //_require.Nil(err) + // + //_, err = containerClient2.Create(ctx, nil) + //_require.Nil(err) +}*/ diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index f4b1c4799ffc..9ddabdfd59e2 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -21,6 +21,16 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } +// UserDelegationCredential contains an account's name and its user delegation key. +type UserDelegationCredential = exported.UserDelegationCredential + +// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it +func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { + return exported.NewUserDelegationCredential(accountName, key) +} + +type GetUserDelegationKeyOptions = generated.ServiceClientGetUserDelegationKeyOptions + // AccessConditions identifies container-specific access conditions which you optionally set. type AccessConditions = exported.ContainerAccessConditions diff --git a/sdk/storage/azblob/service/responses.go b/sdk/storage/azblob/service/responses.go index 1eb5ab3d5ffd..b3e081dfe56b 100644 --- a/sdk/storage/azblob/service/responses.go +++ b/sdk/storage/azblob/service/responses.go @@ -33,3 +33,6 @@ type GetStatisticsResponse = generated.ServiceClientGetStatisticsResponse // FilterBlobsResponse contains the response from method Client.FilterBlobs. type FilterBlobsResponse = generated.ServiceClientFilterBlobsResponse + +// GetUserDelegationKeyResponse contains the response from method ServiceClient.GetUserDelegationKey. +type GetUserDelegationKeyResponse = generated.ServiceClientGetUserDelegationKeyResponse diff --git a/sdk/storage/azblob/zt_examples_test.go b/sdk/storage/azblob/zt_examples_test.go index c02ad7a47aaa..10e482806426 100644 --- a/sdk/storage/azblob/zt_examples_test.go +++ b/sdk/storage/azblob/zt_examples_test.go @@ -13,6 +13,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" "io" "log" "net/http" @@ -363,6 +364,52 @@ func Example_container_NewClientWithSharedKeyCredential() { fmt.Println(containerClient.URL()) } +func Example_service_Client_NewClientWithUserDelegationCredential() { + accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME") + if !ok { + panic("AZURE_STORAGE_ACCOUNT_NAME could not be found") + } + // Create Managed Identity (OAuth) Credentials using Client ID + clientOptions := azcore.ClientOptions{} + optsClientID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ClientID("7cf7db0d-...")} + cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) + if err != nil { + log.Fatal(err) + } + clientOptionsAzBlob := azblob.ClientOptions{} // Same as azcore.ClientOptions using azblob instead + + svcClient, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &clientOptionsAzBlob) + + // Set current and past time and create key + currentTime := time.Now().UTC().Add(-10 * time.Second) + pastTime := currentTime.Add(48 * time.Hour) + info := generated.KeyInfo{ + Start: to.Ptr(currentTime.UTC().Format(azblob.SASTimeFormat)), + Expiry: to.Ptr(pastTime.UTC().Format(azblob.SASTimeFormat)), + } + + _, err = service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + if err != nil { + log.Fatal(err) + } + fmt.Println("User Delegation Key has been created for ", accountName) + + // Create Managed Identity (OAuth) Credentials using Resource ID + optsResourceID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ResourceID("/subscriptions/...")} + cred, err = azidentity.NewManagedIdentityCredential(&optsResourceID) + if err != nil { + log.Fatal(err) + } + + svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) + + _, err = service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + if err != nil { + log.Fatal(err) + } + fmt.Println("User Delegation Key has been created for ", accountName) +} + func Example_container_NewClientWithNoCredential() { accountName, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_NAME") if !ok { From 548a3e05b75dfbafc3434fdb16ca578837f84258 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:46:28 -0700 Subject: [PATCH 02/22] Adding GetUDKSASURL --- sdk/storage/azblob/blob/models.go | 2 + sdk/storage/azblob/common.go | 3 - sdk/storage/azblob/container/client.go | 44 +++++++ sdk/storage/azblob/container/models.go | 2 + sdk/storage/azblob/internal/base/clients.go | 4 + .../azblob/internal/exported/account_sas.go | 72 ++++++++++- .../azblob/internal/exported/service_sas.go | 121 +++++++++++++++++- .../exported/storage_account_credential.go | 16 --- .../exported/user_delegation_credential.go | 18 +-- sdk/storage/azblob/service/client.go | 37 ++++++ 10 files changed, 283 insertions(+), 36 deletions(-) delete mode 100644 sdk/storage/azblob/internal/exported/storage_account_credential.go diff --git a/sdk/storage/azblob/blob/models.go b/sdk/storage/azblob/blob/models.go index 462b8299f11e..ec2ef2617190 100644 --- a/sdk/storage/azblob/blob/models.go +++ b/sdk/storage/azblob/blob/models.go @@ -23,6 +23,8 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } +type UserDelegationKey = generated.UserDelegationKey + // Type Declarations --------------------------------------------------------------------- // AccessConditions identifies blob-specific access conditions which you optionally set. diff --git a/sdk/storage/azblob/common.go b/sdk/storage/azblob/common.go index cc038aaf1c14..2d8c678978d3 100644 --- a/sdk/storage/azblob/common.go +++ b/sdk/storage/azblob/common.go @@ -30,9 +30,6 @@ func NewUserDelegationCredential(accountName string, key generated.UserDelegatio type UserDelegationKey = generated.UserDelegationKey -// StorageAccountCredential is a wrapper interface for SharedKeyCredential and UserDelegationCredential -type StorageAccountCredential = exported.StorageAccountCredential - // IPEndpointStyleInfo is used for IP endpoint style URL when working with Azure storage emulator. // Ex: "https://10.132.141.33/accountname/containername" type IPEndpointStyleInfo = exported.IPEndpointStyleInfo diff --git a/sdk/storage/azblob/container/client.go b/sdk/storage/azblob/container/client.go index 558779ef3544..799212484d12 100644 --- a/sdk/storage/azblob/container/client.go +++ b/sdk/storage/azblob/container/client.go @@ -9,6 +9,7 @@ package container import ( "context" "errors" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "net/http" "strings" "time" @@ -89,6 +90,10 @@ func (c *Client) sharedKey() *SharedKeyCredential { return base.SharedKey((*base.Client[generated.ContainerClient])(c)) } +func (c *Client) userDelegationKey() *UserDelegationKey { + return base.UserDelegationKey((*base.Client[generated.ContainerClient])(c)) +} + // URL returns the URL endpoint used by the Client object. func (c *Client) URL() string { return c.generated().Endpoint() @@ -307,3 +312,42 @@ func (c *Client) GetSASURL(permissions SASPermissions, start time.Time, expiry t return endpoint, nil } + +// GetUDKSASURL is a convenience method for generating a SAS token for the currently pointed at container. +// It can only be used if the credential supplied during creation was a UserDelegationKey. +func (c *Client) GetUDKSASURL(permissions SASPermissions, start time.Time, expiry time.Time) (string, error) { + if c.userDelegationKey() == nil { + return "", errors.New("GetUDKSASURL can only be signed with a UserDelegationKey") + } + + urlParts, err := exported.ParseURL(c.URL()) + if err != nil { + return "", err + } + + udc := azblob.UserDelegationCredential{ + Name: urlParts.Host, + Key: *c.userDelegationKey(), + } + + // Containers do not have snapshots, nor versions. + qps, err := SASSignatureValues{ + Version: exported.SASVersion, + Protocol: exported.SASProtocolHTTPS, + ContainerName: urlParts.ContainerName, + Permissions: permissions.String(), + StartTime: start.UTC(), + ExpiryTime: expiry.UTC(), + }.SignUDK(&udc) + if err != nil { + return "", err + } + + endpoint := c.URL() + if !strings.HasSuffix(endpoint, "/") { + endpoint += "/" + } + endpoint += "?" + qps.Encode() + + return endpoint, nil +} diff --git a/sdk/storage/azblob/container/models.go b/sdk/storage/azblob/container/models.go index 99d4480bf3b5..26e9d07c37ec 100644 --- a/sdk/storage/azblob/container/models.go +++ b/sdk/storage/azblob/container/models.go @@ -20,6 +20,8 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } +type UserDelegationKey = exported.UserDelegationKey + // Request Model Declaration ------------------------------------------------------------------------------------------- // CpkScopeInfo contains a group of parameters for the ContainerClient.Create method. diff --git a/sdk/storage/azblob/internal/base/clients.go b/sdk/storage/azblob/internal/base/clients.go index 23cfacc437bc..80c498ebd12c 100644 --- a/sdk/storage/azblob/internal/base/clients.go +++ b/sdk/storage/azblob/internal/base/clients.go @@ -26,6 +26,10 @@ func SharedKey[T any](client *Client[T]) *exported.SharedKeyCredential { return client.sharedKey } +func UserDelegationKey[T any](client *Client[T]) *exported.UserDelegationKey { + return client.userDelegationKey +} + func NewClient[T any](inner *T) *Client[T] { return &Client[T]{inner: inner} } diff --git a/sdk/storage/azblob/internal/exported/account_sas.go b/sdk/storage/azblob/internal/exported/account_sas.go index 7af9845bd3ec..ba0d099020a3 100644 --- a/sdk/storage/azblob/internal/exported/account_sas.go +++ b/sdk/storage/azblob/internal/exported/account_sas.go @@ -27,7 +27,7 @@ type AccountSASSignatureValues struct { ResourceTypes string `param:"srt"` // Create by initializing AccountSASResourceTypes and then call String() } -// Sign uses an account's shared key credential to sign this signature values to produce +// Sign uses an account's SharedKeyCredential to sign this signature values to produce // the proper SAS query parameters. func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS @@ -82,6 +82,76 @@ func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential return p, nil } +// SignUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. +func (v AccountSASSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (SASQueryParameters, error) { + if userDelegationCredential == nil { + return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without UserDelegationKey") + } + + // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS + if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { + return SASQueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType") + } + if v.Version == "" { + v.Version = SASVersion + } + perms := &AccountSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + + startTime, expiryTime, _ := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, time.Time{}) + + stringToSign := strings.Join([]string{ + userDelegationCredential.AccountName(), + v.Permissions, + v.Services, + v.ResourceTypes, + startTime, + expiryTime, + v.IPRange.String(), + string(v.Protocol), + v.Version, + ""}, // That is right, the account SAS requires a terminating extra newline + "\n") + + signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) + if err != nil { + return SASQueryParameters{}, err + } + p := SASQueryParameters{ + // Common SAS parameters + version: v.Version, + protocol: v.Protocol, + startTime: v.StartTime, + expiryTime: v.ExpiryTime, + permissions: v.Permissions, + ipRange: v.IPRange, + + // Account-specific SAS parameters + services: v.Services, + resourceTypes: v.ResourceTypes, + + // Calculated SAS signature + signature: signature, + } + + udk := userDelegationCredential.getUDKParams() + + //User delegation SAS specific parameters + if udk != nil { + p.signedOID = *udk.SignedOID + p.signedTID = *udk.SignedTID + p.signedStart = *udk.SignedStart + p.signedExpiry = *udk.SignedExpiry + p.signedService = *udk.SignedService + p.signedVersion = *udk.SignedVersion + } + + return p, nil +} + // AccountSASPermissions type simplifies creating the permissions string for an Azure Storage Account SAS. // Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Permissions field. type AccountSASPermissions struct { diff --git a/sdk/storage/azblob/internal/exported/service_sas.go b/sdk/storage/azblob/internal/exported/service_sas.go index 50ec7443bd82..64e9062d27cc 100644 --- a/sdk/storage/azblob/internal/exported/service_sas.go +++ b/sdk/storage/azblob/internal/exported/service_sas.go @@ -45,11 +45,10 @@ func getDirectoryDepth(path string) string { return fmt.Sprint(strings.Count(path, "/") + 1) } -// Sign uses an account's StorageAccountCredential to sign this signature values to produce the proper SAS query parameters. -// See: StorageAccountCredential. Compatible with both UserDelegationCredential and SharedKeyCredential -func (v BlobSASSignatureValues) Sign(credential StorageAccountCredential) (SASQueryParameters, error) { +// Sign uses an account's SharedKeyCredential to sign this signature values to produce the proper SAS query parameters. +func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { resource := "c" - if credential == nil { + if sharedKeyCredential == nil { return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without Shared Key Credential") } @@ -100,7 +99,115 @@ func (v BlobSASSignatureValues) Sign(credential StorageAccountCredential) (SASQu signedIdentifier := v.Identifier - udk := credential.getUDKParams() + // String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx + stringToSign := strings.Join([]string{ + v.Permissions, + startTime, + expiryTime, + getCanonicalName(sharedKeyCredential.AccountName(), v.ContainerName, v.BlobName, v.Directory), + signedIdentifier, + v.IPRange.String(), + string(v.Protocol), + v.Version, + resource, + snapshotTime, // signed timestamp + v.CacheControl, // rscc + v.ContentDisposition, // rscd + v.ContentEncoding, // rsce + v.ContentLanguage, // rscl + v.ContentType}, // rsct + "\n") + + signature := "" + signature, err := sharedKeyCredential.ComputeHMACSHA256(stringToSign) + if err != nil { + return SASQueryParameters{}, err + } + + p := SASQueryParameters{ + // Common SAS parameters + version: v.Version, + protocol: v.Protocol, + startTime: v.StartTime, + expiryTime: v.ExpiryTime, + permissions: v.Permissions, + ipRange: v.IPRange, + + // Container/Blob-specific SAS parameters + resource: resource, + identifier: v.Identifier, + cacheControl: v.CacheControl, + contentDisposition: v.ContentDisposition, + contentEncoding: v.ContentEncoding, + contentLanguage: v.ContentLanguage, + contentType: v.ContentType, + snapshotTime: v.SnapshotTime, + signedDirectoryDepth: getDirectoryDepth(v.Directory), + preauthorizedAgentObjectID: v.PreauthorizedAgentObjectId, + agentObjectID: v.AgentObjectId, + correlationID: v.CorrelationId, + // Calculated SAS signature + signature: signature, + } + + return p, nil +} + +// SignUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. +func (v BlobSASSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (SASQueryParameters, error) { + resource := "c" + if userDelegationCredential == nil { + return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without UserDelegationKey") + } + + if !v.SnapshotTime.IsZero() { + resource = "bs" + //Make sure the permission characters are in the correct order + perms := &BlobSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + } else if v.BlobVersion != "" { + resource = "bv" + //Make sure the permission characters are in the correct order + perms := &BlobSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + } else if v.Directory != "" { + resource = "d" + v.BlobName = "" + perms := &BlobSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + } else if v.BlobName == "" { + // Make sure the permission characters are in the correct order + perms := &ContainerSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + } else { + resource = "b" + // Make sure the permission characters are in the correct order + perms := &BlobSASPermissions{} + if err := perms.Parse(v.Permissions); err != nil { + return SASQueryParameters{}, err + } + v.Permissions = perms.String() + } + if v.Version == "" { + v.Version = SASVersion + } + startTime, expiryTime, snapshotTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, v.SnapshotTime) + + signedIdentifier := v.Identifier + + udk := userDelegationCredential.getUDKParams() if udk != nil { udkStart, udkExpiry, _ := FormatTimesForSASSigning(*udk.SignedStart, *udk.SignedExpiry, time.Time{}) @@ -122,7 +229,7 @@ func (v BlobSASSignatureValues) Sign(credential StorageAccountCredential) (SASQu v.Permissions, startTime, expiryTime, - getCanonicalName(credential.AccountName(), v.ContainerName, v.BlobName, v.Directory), + getCanonicalName(userDelegationCredential.AccountName(), v.ContainerName, v.BlobName, v.Directory), signedIdentifier, v.IPRange.String(), string(v.Protocol), @@ -137,7 +244,7 @@ func (v BlobSASSignatureValues) Sign(credential StorageAccountCredential) (SASQu "\n") signature := "" - signature, err := credential.ComputeHMACSHA256(stringToSign) + signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) if err != nil { return SASQueryParameters{}, err } diff --git a/sdk/storage/azblob/internal/exported/storage_account_credential.go b/sdk/storage/azblob/internal/exported/storage_account_credential.go deleted file mode 100644 index 66027f906112..000000000000 --- a/sdk/storage/azblob/internal/exported/storage_account_credential.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build go1.18 -// +build go1.18 - -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -package exported - -import "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" - -// StorageAccountCredential is a wrapper interface for SharedKeyCredential and UserDelegationCredential -type StorageAccountCredential interface { - AccountName() string - ComputeHMACSHA256(message string) (string, error) - getUDKParams() *generated.UserDelegationKey -} diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index 2cf6bcf629ad..d7a66c7b6d31 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -13,27 +13,27 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" ) -// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it +// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's Name and a user delegation Key from it func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { return &UserDelegationCredential{ - accountName: accountName, - accountKey: key, + Name: accountName, + Key: key, } } type UserDelegationCredential struct { - accountName string - accountKey generated.UserDelegationKey + Name string + Key generated.UserDelegationKey } -// AccountName returns the Storage account's name +// AccountName returns the Storage account's Name func (f *UserDelegationCredential) AccountName() string { - return f.accountName + return f.Name } // ComputeHMAC func (f *UserDelegationCredential) ComputeHMACSHA256(message string) (string, error) { - bytes, _ := base64.StdEncoding.DecodeString(*f.accountKey.Value) + bytes, _ := base64.StdEncoding.DecodeString(*f.Key.Value) h := hmac.New(sha256.New, bytes) _, err := h.Write([]byte(message)) return base64.StdEncoding.EncodeToString(h.Sum(nil)), err @@ -41,7 +41,7 @@ func (f *UserDelegationCredential) ComputeHMACSHA256(message string) (string, er // Private method to return important parameters for NewSASQueryParameters func (f *UserDelegationCredential) getUDKParams() *generated.UserDelegationKey { - return &f.accountKey + return &f.Key } type UserDelegationKey = generated.UserDelegationKey diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index 3a3b4d2bfc2a..92c4c849649e 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -115,6 +115,10 @@ func (s *Client) sharedKey() *SharedKeyCredential { return base.SharedKey((*base.Client[generated.ServiceClient])(s)) } +func (s *Client) userDelegationKey() *generated.UserDelegationKey { + return base.UserDelegationKey((*base.Client[generated.ServiceClient])(s)) +} + // URL returns the URL endpoint used by the Client object. func (s *Client) URL() string { return s.generated().Endpoint() @@ -266,6 +270,39 @@ func (s *Client) GetSASURL(resources SASResourceTypes, permissions SASPermission return endpoint, nil } +// GetUDKSASURL is a convenience method for generating a SAS token for the currently pointed at account. +// It can only be used if the credential supplied during creation was a UserDelegationKey. +// This validity can be checked with CanGetAccountSASToken(). +func (s *Client) GetUDKSASURL(resources SASResourceTypes, permissions SASPermissions, services SASServices, start time.Time, expiry time.Time) (string, error) { + if s.userDelegationKey() == nil { + return "", errors.New("SAS can only be signed with a SharedKeyCredential") + } + + udc := UserDelegationCredential{ + Key: *s.userDelegationKey(), + } + qps, err := SASSignatureValues{ + Version: exported.SASVersion, + Protocol: exported.SASProtocolHTTPS, + Permissions: permissions.String(), + Services: services.String(), + ResourceTypes: resources.String(), + StartTime: start.UTC(), + ExpiryTime: expiry.UTC(), + }.SignUDK(&udc) + if err != nil { + return "", err + } + + endpoint := s.URL() + if !strings.HasSuffix(endpoint, "/") { + endpoint += "/" + } + endpoint += "?" + qps.Encode() + + return endpoint, nil +} + // FilterBlobs operation finds all blobs in the storage account whose tags match a given search expression. // Filter blobs searches across all containers within a storage account but can be scoped within the expression to a single container. // https://docs.microsoft.com/en-us/rest/api/storageservices/find-blobs-by-tags From 361c25ac7d3675b78302d0e5046f23b106ea9cd5 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:04:14 -0700 Subject: [PATCH 03/22] Removing changes as requested --- .../internal/blob/zc_sas_account.go | 73 +------- .../internal/blob/zc_sas_service.go | 169 ++++-------------- 2 files changed, 34 insertions(+), 208 deletions(-) diff --git a/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go b/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go index 7a506a0e23a3..278a08c7147d 100644 --- a/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go +++ b/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go @@ -27,8 +27,7 @@ type AccountSASSignatureValues struct { ResourceTypes string `param:"srt"` // Create by initializing AccountSASResourceTypes and then call String() } -// Sign uses an account's SharedKeyCredential to sign this signature values to produce -// the proper SAS query parameters. +// Sign uses an account's shared key credential to sign this signature values to produce func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { @@ -82,76 +81,6 @@ func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential return p, nil } -// SignUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. -func (v AccountSASSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (SASQueryParameters, error) { - if userDelegationCredential == nil { - return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without UserDelegationKey") - } - - // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS - if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { - return SASQueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType") - } - if v.Version == "" { - v.Version = SASVersion - } - perms := &AccountSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - - startTime, expiryTime, _ := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, time.Time{}) - - stringToSign := strings.Join([]string{ - userDelegationCredential.AccountName(), - v.Permissions, - v.Services, - v.ResourceTypes, - startTime, - expiryTime, - v.IPRange.String(), - string(v.Protocol), - v.Version, - ""}, // That is right, the account SAS requires a terminating extra newline - "\n") - - signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) - if err != nil { - return SASQueryParameters{}, err - } - p := SASQueryParameters{ - // Common SAS parameters - version: v.Version, - protocol: v.Protocol, - startTime: v.StartTime, - expiryTime: v.ExpiryTime, - permissions: v.Permissions, - ipRange: v.IPRange, - - // Account-specific SAS parameters - services: v.Services, - resourceTypes: v.ResourceTypes, - - // Calculated SAS signature - signature: signature, - } - - udk := userDelegationCredential.getUDKParams() - - //User delegation SAS specific parameters - if udk != nil { - p.signedOID = *udk.SignedOID - p.signedTID = *udk.SignedTID - p.signedStart = *udk.SignedStart - p.signedExpiry = *udk.SignedExpiry - p.signedService = *udk.SignedService - p.signedVersion = *udk.SignedVersion - } - - return p, nil -} - // AccountSASPermissions type simplifies creating the permissions string for an Azure Storage Account SAS. // Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Permissions field. type AccountSASPermissions struct { diff --git a/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go b/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go index 12c2f9ea989a..f47e1afe55a4 100644 --- a/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go +++ b/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go @@ -45,8 +45,11 @@ func getDirectoryDepth(path string) string { return fmt.Sprint(strings.Count(path, "/") + 1) } -// Sign uses an account's SharedKeyCredential to sign this signature values to produce the proper SAS query parameters. -func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { +// NewSASQueryParameters uses an account's StorageAccountCredential to sign this signature values to produce +// the proper SAS query parameters. + +// See: StorageAccountCredential. Compatible with both UserDelegationCredential and SharedKeyCredential +func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { resource := "c" if sharedKeyCredential == nil { return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without Shared Key Credential") @@ -99,6 +102,25 @@ func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( signedIdentifier := v.Identifier + //udk := sharedKeyCredential.getUDKParams() + // + //if udk != nil { + // udkStart, udkExpiry, _ := FormatTimesForSASSigning(udk.SignedStart, udk.SignedExpiry, time.Time{}) + // //I don't like this answer to combining the functions + // //But because signedIdentifier and the user delegation key strings share a place, this is an _OK_ way to do it. + // signedIdentifier = strings.Join([]string{ + // udk.SignedOid, + // udk.SignedTid, + // udkStart, + // udkExpiry, + // udk.SignedService, + // udk.SignedVersion, + // v.PreauthorizedAgentObjectId, + // v.AgentObjectId, + // v.CorrelationId, + // }, "\n") + //} + // String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx stringToSign := strings.Join([]string{ v.Permissions, @@ -150,140 +172,15 @@ func (v BlobSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( signature: signature, } - return p, nil -} - -// SignUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. -func (v BlobSASSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (SASQueryParameters, error) { - resource := "c" - if userDelegationCredential == nil { - return SASQueryParameters{}, fmt.Errorf("cannot sign SAS query without UserDelegationKey") - } - - if !v.SnapshotTime.IsZero() { - resource = "bs" - //Make sure the permission characters are in the correct order - perms := &BlobSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - } else if v.BlobVersion != "" { - resource = "bv" - //Make sure the permission characters are in the correct order - perms := &BlobSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - } else if v.Directory != "" { - resource = "d" - v.BlobName = "" - perms := &BlobSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - } else if v.BlobName == "" { - // Make sure the permission characters are in the correct order - perms := &ContainerSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - } else { - resource = "b" - // Make sure the permission characters are in the correct order - perms := &BlobSASPermissions{} - if err := perms.Parse(v.Permissions); err != nil { - return SASQueryParameters{}, err - } - v.Permissions = perms.String() - } - if v.Version == "" { - v.Version = SASVersion - } - startTime, expiryTime, snapshotTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime, v.SnapshotTime) - - signedIdentifier := v.Identifier - - udk := userDelegationCredential.getUDKParams() - - if udk != nil { - udkStart, udkExpiry, _ := FormatTimesForSASSigning(*udk.SignedStart, *udk.SignedExpiry, time.Time{}) - signedIdentifier = strings.Join([]string{ - *udk.SignedOID, - *udk.SignedTID, - udkStart, - udkExpiry, - *udk.SignedService, - *udk.SignedVersion, - v.PreauthorizedAgentObjectId, - v.AgentObjectId, - v.CorrelationId, - }, "\n") - } - - // String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx - stringToSign := strings.Join([]string{ - v.Permissions, - startTime, - expiryTime, - getCanonicalName(userDelegationCredential.AccountName(), v.ContainerName, v.BlobName, v.Directory), - signedIdentifier, - v.IPRange.String(), - string(v.Protocol), - v.Version, - resource, - snapshotTime, // signed timestamp - v.CacheControl, // rscc - v.ContentDisposition, // rscd - v.ContentEncoding, // rsce - v.ContentLanguage, // rscl - v.ContentType}, // rsct - "\n") - - signature := "" - signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) - if err != nil { - return SASQueryParameters{}, err - } - - p := SASQueryParameters{ - // Common SAS parameters - version: v.Version, - protocol: v.Protocol, - startTime: v.StartTime, - expiryTime: v.ExpiryTime, - permissions: v.Permissions, - ipRange: v.IPRange, - - // Container/Blob-specific SAS parameters - resource: resource, - identifier: v.Identifier, - cacheControl: v.CacheControl, - contentDisposition: v.ContentDisposition, - contentEncoding: v.ContentEncoding, - contentLanguage: v.ContentLanguage, - contentType: v.ContentType, - snapshotTime: v.SnapshotTime, - signedDirectoryDepth: getDirectoryDepth(v.Directory), - preauthorizedAgentObjectID: v.PreauthorizedAgentObjectId, - agentObjectID: v.AgentObjectId, - correlationID: v.CorrelationId, - // Calculated SAS signature - signature: signature, - } - - //User delegation SAS specific parameters - if udk != nil { - p.signedOID = *udk.SignedOID - p.signedTID = *udk.SignedTID - p.signedStart = *udk.SignedStart - p.signedExpiry = *udk.SignedExpiry - p.signedService = *udk.SignedService - p.signedVersion = *udk.SignedVersion - } + ////User delegation SAS specific parameters + //if udk != nil { + // p.signedOid = udk.SignedOid + // p.signedTid = udk.SignedTid + // p.signedStart = udk.SignedStart + // p.signedExpiry = udk.SignedExpiry + // p.signedService = udk.SignedService + // p.signedVersion = udk.SignedVersion + //} return p, nil } From 3e8904c2ebf31c6e25a905bb06c588bc28a664ea Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:05:33 -0700 Subject: [PATCH 04/22] Additional changes to remove --- sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go | 1 + sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go b/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go index 278a08c7147d..95842d88f2f6 100644 --- a/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go +++ b/sdk/messaging/azeventhubs/internal/blob/zc_sas_account.go @@ -28,6 +28,7 @@ type AccountSASSignatureValues struct { } // Sign uses an account's shared key credential to sign this signature values to produce +// the proper SAS query parameters. func (v AccountSASSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { diff --git a/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go b/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go index f47e1afe55a4..826ffee84ef0 100644 --- a/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go +++ b/sdk/messaging/azeventhubs/internal/blob/zc_sas_service.go @@ -47,7 +47,6 @@ func getDirectoryDepth(path string) string { // NewSASQueryParameters uses an account's StorageAccountCredential to sign this signature values to produce // the proper SAS query parameters. - // See: StorageAccountCredential. Compatible with both UserDelegationCredential and SharedKeyCredential func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) (SASQueryParameters, error) { resource := "c" From 1a628663b931b738e413b2b0bc122014f7fb3273 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Wed, 21 Sep 2022 16:27:09 -0700 Subject: [PATCH 05/22] Addressing comments, cleaning up code, adding wip test --- sdk/storage/azblob/blob/client.go | 2 +- sdk/storage/azblob/blob/client_test.go | 4 +- sdk/storage/azblob/blockblob/client_test.go | 2 +- sdk/storage/azblob/common.go | 4 +- sdk/storage/azblob/constants.go | 3 - sdk/storage/azblob/container/client.go | 46 +------------- sdk/storage/azblob/internal/base/clients.go | 33 +--------- .../exported/user_delegation_credential.go | 4 +- sdk/storage/azblob/sas/account.go | 8 +-- sdk/storage/azblob/sas/service.go | 8 +-- sdk/storage/azblob/service/client.go | 45 ++----------- sdk/storage/azblob/service/client_test.go | 63 +++++++++---------- sdk/storage/azblob/service/examples_test.go | 10 +-- sdk/storage/azblob/service/models.go | 4 +- 14 files changed, 62 insertions(+), 174 deletions(-) diff --git a/sdk/storage/azblob/blob/client.go b/sdk/storage/azblob/blob/client.go index 054a48063e50..90e101806490 100644 --- a/sdk/storage/azblob/blob/client.go +++ b/sdk/storage/azblob/blob/client.go @@ -255,7 +255,7 @@ func (b *Client) GetSASURL(permissions sas.BlobPermissions, start time.Time, exp StartTime: start.UTC(), ExpiryTime: expiry.UTC(), - }.Sign(b.sharedKey()) + }.SignWithSharedKey(b.sharedKey()) if err != nil { return "", err diff --git a/sdk/storage/azblob/blob/client_test.go b/sdk/storage/azblob/blob/client_test.go index c5f6b36acb88..e6fb13f135ec 100644 --- a/sdk/storage/azblob/blob/client_test.go +++ b/sdk/storage/azblob/blob/client_test.go @@ -114,7 +114,7 @@ func (s *BlobUnrecordedTestsSuite) TestCreateBlobClientWithSnapshotAndSAS() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.Sign(credential) + }.SignWithSharedKey(credential) _require.Nil(err) parts, err := blob.ParseURL(bbClient.URL()) @@ -156,7 +156,7 @@ func (s *BlobUnrecordedTestsSuite) TestCreateBlobClientWithSnapshotAndSASUsingCo Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.Sign(credential) + }.SignWithSharedKey(credential) _require.Nil(err) parts, err := blob.ParseURL(bbClient.URL()) diff --git a/sdk/storage/azblob/blockblob/client_test.go b/sdk/storage/azblob/blockblob/client_test.go index c300da7d03c6..724b56d14800 100644 --- a/sdk/storage/azblob/blockblob/client_test.go +++ b/sdk/storage/azblob/blockblob/client_test.go @@ -1166,7 +1166,7 @@ func (s *BlockBlobUnrecordedTestsSuite) TestSetTierOnCopyBlockBlobFromURL() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.Sign(credential) + }.SignWithSharedKey(credential) _require.Nil(err) srcBlobParts, _ := blob.ParseURL(srcBlob.URL()) diff --git a/sdk/storage/azblob/common.go b/sdk/storage/azblob/common.go index 23354e0de2c7..dcc102544f22 100644 --- a/sdk/storage/azblob/common.go +++ b/sdk/storage/azblob/common.go @@ -25,8 +25,8 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia type UserDelegationCredential = exported.UserDelegationCredential // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { - return exported.NewUserDelegationCredential(accountName, key) +func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { + return exported.NewUserDelegationCredential(accountName, udk) } // UserDelegationKey contains UserDelegationKey. diff --git a/sdk/storage/azblob/constants.go b/sdk/storage/azblob/constants.go index 4fe921e17603..d4c3262d932d 100644 --- a/sdk/storage/azblob/constants.go +++ b/sdk/storage/azblob/constants.go @@ -7,7 +7,6 @@ package azblob import ( - "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" ) @@ -36,5 +35,3 @@ const ( func PossibleDeleteSnapshotsOptionTypeValues() []DeleteSnapshotsOptionType { return generated.PossibleDeleteSnapshotsOptionTypeValues() } - -const SASTimeFormat = exported.SASTimeFormat // "2017-07-27T00:00:00Z" // ISO 8601 diff --git a/sdk/storage/azblob/container/client.go b/sdk/storage/azblob/container/client.go index e3c0312f9819..032d3ba1d863 100644 --- a/sdk/storage/azblob/container/client.go +++ b/sdk/storage/azblob/container/client.go @@ -9,7 +9,6 @@ package container import ( "context" "errors" - "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "net/http" "strings" "time" @@ -91,9 +90,9 @@ func (c *Client) sharedKey() *SharedKeyCredential { return base.SharedKey((*base.Client[generated.ContainerClient])(c)) } -func (c *Client) userDelegationKey() *UserDelegationKey { +/*func (c *Client) userDelegationKey() *UserDelegationKey { return base.UserDelegationKey((*base.Client[generated.ContainerClient])(c)) -} +}*/ // URL returns the URL endpoint used by the Client object. func (c *Client) URL() string { @@ -300,46 +299,7 @@ func (c *Client) GetSASURL(permissions sas.ContainerPermissions, start time.Time Permissions: permissions.String(), StartTime: start.UTC(), ExpiryTime: expiry.UTC(), - }.Sign(c.sharedKey()) - if err != nil { - return "", err - } - - endpoint := c.URL() - if !strings.HasSuffix(endpoint, "/") { - endpoint += "/" - } - endpoint += "?" + qps.Encode() - - return endpoint, nil -} - -// GetUDKSASURL is a convenience method for generating a SAS token for the currently pointed at container. -// It can only be used if the credential supplied during creation was a UserDelegationKey. -func (c *Client) GetUDKSASURL(permissions sas.ContainerPermissions, start time.Time, expiry time.Time) (string, error) { - if c.userDelegationKey() == nil { - return "", errors.New("GetUDKSASURL can only be signed with a UserDelegationKey") - } - - urlParts, err := blob.ParseURL(c.URL()) - if err != nil { - return "", err - } - - udc := azblob.UserDelegationCredential{ - Name: urlParts.Host, - Key: *c.userDelegationKey(), - } - - // Containers do not have snapshots, nor versions. - qps, err := sas.BlobSignatureValues{ - Version: sas.Version, - Protocol: sas.ProtocolHTTPS, - ContainerName: urlParts.ContainerName, - Permissions: permissions.String(), - StartTime: start.UTC(), - ExpiryTime: expiry.UTC(), - }.SignUDK(&udc) + }.SignWithSharedKey(c.sharedKey()) if err != nil { return "", err } diff --git a/sdk/storage/azblob/internal/base/clients.go b/sdk/storage/azblob/internal/base/clients.go index 80c498ebd12c..16e6cac066db 100644 --- a/sdk/storage/azblob/internal/base/clients.go +++ b/sdk/storage/azblob/internal/base/clients.go @@ -13,9 +13,8 @@ import ( ) type Client[T any] struct { - inner *T - sharedKey *exported.SharedKeyCredential - userDelegationKey *exported.UserDelegationKey + inner *T + sharedKey *exported.SharedKeyCredential } func InnerClient[T any](client *Client[T]) *T { @@ -26,10 +25,6 @@ func SharedKey[T any](client *Client[T]) *exported.SharedKeyCredential { return client.sharedKey } -func UserDelegationKey[T any](client *Client[T]) *exported.UserDelegationKey { - return client.userDelegationKey -} - func NewClient[T any](inner *T) *Client[T] { return &Client[T]{inner: inner} } @@ -55,30 +50,6 @@ func NewBlobClient(blobURL string, pipeline runtime.Pipeline, sharedKey *exporte } } -func NewServiceClientUDK(containerURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.ServiceClient] { - return &Client[generated.ServiceClient]{ - inner: generated.NewServiceClient(containerURL, pipeline), - sharedKey: sharedKey, - userDelegationKey: udk, - } -} - -func NewContainerClientUDK(containerURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.ContainerClient] { - return &Client[generated.ContainerClient]{ - inner: generated.NewContainerClient(containerURL, pipeline), - sharedKey: sharedKey, - userDelegationKey: udk, - } -} - -func NewBlobClientUDK(blobURL string, pipeline runtime.Pipeline, sharedKey *exported.SharedKeyCredential, udk *exported.UserDelegationKey) *Client[generated.BlobClient] { - return &Client[generated.BlobClient]{ - inner: generated.NewBlobClient(blobURL, pipeline), - sharedKey: sharedKey, - userDelegationKey: udk, - } -} - type CompositeClient[T, U any] struct { innerT *T innerU *U diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index 2925871b6f9e..d9d8205106f9 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -14,10 +14,10 @@ import ( ) // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's Name and a user delegation Key from it -func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { +func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { return &UserDelegationCredential{ Name: accountName, - Key: key, + Key: udk, } } diff --git a/sdk/storage/azblob/sas/account.go b/sdk/storage/azblob/sas/account.go index dc6f61898210..a2ff7f536fd8 100644 --- a/sdk/storage/azblob/sas/account.go +++ b/sdk/storage/azblob/sas/account.go @@ -35,9 +35,9 @@ type AccountSignatureValues struct { ResourceTypes string `param:"srt"` // Create by initializing AccountSASResourceTypes and then call String() } -// Sign uses an account's shared key credential to sign this signature values to produce +// SignWithSharedKey uses an account's shared key credential to sign this signature values to produce // the proper SAS query parameters. -func (v AccountSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (QueryParameters, error) { +func (v AccountSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKeyCredential) (QueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { return QueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType") @@ -90,8 +90,8 @@ func (v AccountSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) ( return p, nil } -// SignUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. -func (v AccountSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { +// SignWithUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. +func (v AccountSignatureValues) SignWithUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { return QueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType") diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index 6f74ad9c6529..87b0187cd82a 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -47,8 +47,8 @@ func getDirectoryDepth(path string) string { return fmt.Sprint(strings.Count(path, "/") + 1) } -// Sign uses an account's SharedKeyCredential to sign this signature values to produce the proper SAS query parameters. -func (v BlobSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (QueryParameters, error) { +// SignWithSharedKey uses an account's SharedKeyCredential to sign this signature values to produce the proper SAS query parameters. +func (v BlobSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKeyCredential) (QueryParameters, error) { if sharedKeyCredential == nil { return QueryParameters{}, fmt.Errorf("cannot sign SAS query without Shared Key Credential") } @@ -134,8 +134,8 @@ func (v BlobSignatureValues) Sign(sharedKeyCredential *SharedKeyCredential) (Que return p, nil } -// SignUDK uses an account's UserDelegationCredential to sign this signature values to produce the proper SAS query parameters. -func (v BlobSignatureValues) SignUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { +// SignWithUDK uses an account's UserDelegationCredential to sign this signature values to produce the proper SAS query parameters. +func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { if userDelegationCredential == nil { return QueryParameters{}, fmt.Errorf("cannot sign SAS query without User Delegation Key") } diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index 7a1d022e83d1..e2a03406d36a 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -83,9 +83,9 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio return NewClientWithNoCredential(parsed.ServiceURL, options) } -// NewClientWithUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. +// GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func NewClientWithUserDelegationCredential(serviceURL string, ctx context.Context, info generated.KeyInfo, timeout *int32, requestID *string) (*Client, error) { +func GetUserDelegationCredential(serviceURL string, ctx context.Context, info generated.KeyInfo, timeout *int32, requestID *string) (*UserDelegationCredential, error) { url, err := blob.ParseURL(serviceURL) if err != nil { return nil, err @@ -101,7 +101,7 @@ func NewClientWithUserDelegationCredential(serviceURL string, ctx context.Contex return nil, err } - return (*Client)(base.NewServiceClientUDK(url.Host, pl, nil, &udk.UserDelegationKey)), nil + return NewUserDelegationCredential(url.Host, udk.UserDelegationKey), nil } func (s Client) GetUserDelegationKey(ctx context.Context, keyInfo generated.KeyInfo, options *GetUserDelegationKeyOptions) GetUserDelegationKeyResponse { @@ -117,10 +117,6 @@ func (s *Client) sharedKey() *SharedKeyCredential { return base.SharedKey((*base.Client[generated.ServiceClient])(s)) } -func (s *Client) userDelegationKey() *generated.UserDelegationKey { - return base.UserDelegationKey((*base.Client[generated.ServiceClient])(s)) -} - // URL returns the URL endpoint used by the Client object. func (s *Client) URL() string { return s.generated().Endpoint() @@ -258,40 +254,7 @@ func (s *Client) GetSASURL(resources sas.AccountResourceTypes, permissions sas.A ResourceTypes: resources.String(), StartTime: start.UTC(), ExpiryTime: expiry.UTC(), - }.Sign(s.sharedKey()) - if err != nil { - return "", err - } - - endpoint := s.URL() - if !strings.HasSuffix(endpoint, "/") { - endpoint += "/" - } - endpoint += "?" + qps.Encode() - - return endpoint, nil -} - -// GetUDKSASURL is a convenience method for generating a SAS token for the currently pointed at account. -// It can only be used if the credential supplied during creation was a UserDelegationKey. -// This validity can be checked with CanGetAccountSASToken(). -func (s *Client) GetUDKSASURL(resources sas.AccountResourceTypes, permissions sas.AccountPermissions, services sas.AccountServices, start time.Time, expiry time.Time) (string, error) { - if s.userDelegationKey() == nil { - return "", errors.New("SAS can only be signed with a SharedKeyCredential") - } - - udc := UserDelegationCredential{ - Key: *s.userDelegationKey(), - } - qps, err := sas.AccountSignatureValues{ - Version: sas.Version, - Protocol: sas.ProtocolHTTPS, - Permissions: permissions.String(), - Services: services.String(), - ResourceTypes: resources.String(), - StartTime: start.UTC(), - ExpiryTime: expiry.UTC(), - }.SignUDK(&udc) + }.SignWithSharedKey(s.sharedKey()) if err != nil { return "", err } diff --git a/sdk/storage/azblob/service/client_test.go b/sdk/storage/azblob/service/client_test.go index e365151c7c0d..4cab627c6703 100644 --- a/sdk/storage/azblob/service/client_test.go +++ b/sdk/storage/azblob/service/client_test.go @@ -9,7 +9,10 @@ package service_test import ( "context" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" "os" + "strings" "testing" "time" @@ -600,62 +603,56 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() { //_require.Nil(err) } -/*func (s *ServiceRecordedTestsSuite) TestUDKServiceClient() { +func (s *ServiceRecordedTestsSuite) TestUDKServiceClient() { _require := require.New(s.T()) testName := s.T().Name() accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") - clientOptions := azcore.ClientOptions{} - optsClientID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ClientID("7cf7db0d-...")} + optsClientID := azidentity.ManagedIdentityCredentialOptions{ID: azidentity.ClientID("7cf7db0d-...")} cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) _require.Nil(err) svcClient, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &azblob.ClientOptions{}) + _require.Nil(err) - // Set current and past time + // Set current and past time, create KeyInfo currentTime := time.Now().UTC().Add(-10 * time.Second) pastTime := currentTime.Add(48 * time.Hour) - info := azblob.KeyInfo{ - Start: to.Ptr(currentTime.UTC().Format(azblob.SASTimeFormat)), - Expiry: to.Ptr(pastTime.UTC().Format(azblob.SASTimeFormat)), + _require.Nil(err) + info := generated.KeyInfo{ + Start: to.Ptr(currentTime.UTC().Format(sas.TimeFormat)), + Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - serviceClient, err := service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + // Get UserDelegationCredential + udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) _require.Nil(err) - containerName := testcommon.GenerateContainerName(testName) + csas, err := sas.AccountSignatureValues{ + Protocol: sas.ProtocolHTTPS, + ExpiryTime: pastTime.UTC(), + Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), + Services: to.Ptr(sas.AccountServices{Blob: true}).String(), + ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), + }.SignWithUDK(udc) + _require.Nil(err) - resources := service.SASResourceTypes{ - Object: true, - Service: true, - Container: true, - } - permissions := service.SASPermissions{ - Read: true, - Add: true, - Write: true, - Create: true, - Update: true, - Delete: true, - } - services := service.SASServices{ - Blob: true, + sasURL := svcClient.URL() + if !strings.HasSuffix(sasURL, "/") { + sasURL += "/" } - start := time.Now().Add(-time.Hour) - expiry := start.Add(time.Hour) + sasURL += "?" + csas.Encode() - sasUrl, err := serviceClient.GetSASURL(resources, permissions, services, start, expiry) - _require.Nil(err) - - cl, err := service.NewClientWithUserDelegationCredential(sasUrl, nil, info, nil, nil) + containerName := testcommon.GenerateContainerName(testName) + sc, err := service.NewClientWithNoCredential(sasURL, nil) _require.Nil(err) - _, err = cl.CreateContainer(context.Background(), containerName+"002", nil) + _, err = sc.CreateContainer(context.Background(), containerName+"002", nil) _require.Nil(err) - _, err = cl.DeleteContainer(context.Background(), containerName+"002", nil) + _, err = sc.DeleteContainer(context.Background(), containerName+"002", nil) _require.Nil(err) -}*/ +} /*func (s *ServiceUnrecordedTestsSuite) TestUDKContainerClient() { _require := require.New(s.T()) diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index 932eba2d10a7..4a5e6dbf0175 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -235,7 +235,7 @@ func Example_service_SASSignatureValues_Sign() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.Sign(credential) + }.SignWithSharedKey(credential) handleError(err) sasURL := fmt.Sprintf("https://%s.blob.core.windows.net/?%s", accountName, sasQueryParams.Encode()) @@ -269,11 +269,11 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { currentTime := time.Now().UTC().Add(-10 * time.Second) pastTime := currentTime.Add(48 * time.Hour) info := generated.KeyInfo{ - Start: to.Ptr(currentTime.UTC().Format(azblob.SASTimeFormat)), - Expiry: to.Ptr(pastTime.UTC().Format(azblob.SASTimeFormat)), + Start: to.Ptr(currentTime.UTC().Format(sas.TimeFormat)), + Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - _, err = service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + _, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) if err != nil { log.Fatal(err) } @@ -288,7 +288,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) - _, err = service.NewClientWithUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + _, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) if err != nil { log.Fatal(err) } diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index eab35bbf2381..23b8f5aa363d 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -25,8 +25,8 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia type UserDelegationCredential = exported.UserDelegationCredential // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, key generated.UserDelegationKey) *UserDelegationCredential { - return exported.NewUserDelegationCredential(accountName, key) +func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { + return exported.NewUserDelegationCredential(accountName, udk) } type GetUserDelegationKeyOptions = generated.ServiceClientGetUserDelegationKeyOptions From dfca65df84a27243535ddb89a022f83d36241854 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:30:11 -0700 Subject: [PATCH 06/22] Modifications to test --- sdk/storage/azblob/container/client.go | 4 -- sdk/storage/azblob/service/client_test.go | 74 +---------------------- 2 files changed, 2 insertions(+), 76 deletions(-) diff --git a/sdk/storage/azblob/container/client.go b/sdk/storage/azblob/container/client.go index 032d3ba1d863..b715a9cdfd92 100644 --- a/sdk/storage/azblob/container/client.go +++ b/sdk/storage/azblob/container/client.go @@ -90,10 +90,6 @@ func (c *Client) sharedKey() *SharedKeyCredential { return base.SharedKey((*base.Client[generated.ContainerClient])(c)) } -/*func (c *Client) userDelegationKey() *UserDelegationKey { - return base.UserDelegationKey((*base.Client[generated.ContainerClient])(c)) -}*/ - // URL returns the URL endpoint used by the Client object. func (c *Client) URL() string { return c.generated().Endpoint() diff --git a/sdk/storage/azblob/service/client_test.go b/sdk/storage/azblob/service/client_test.go index 4cab627c6703..940ea8df7ffc 100644 --- a/sdk/storage/azblob/service/client_test.go +++ b/sdk/storage/azblob/service/client_test.go @@ -603,12 +603,12 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() { //_require.Nil(err) } -func (s *ServiceRecordedTestsSuite) TestUDKServiceClient() { +func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { _require := require.New(s.T()) testName := s.T().Name() accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") - optsClientID := azidentity.ManagedIdentityCredentialOptions{ID: azidentity.ClientID("7cf7db0d-...")} + optsClientID := azidentity.ManagedIdentityCredentialOptions{ID: azidentity.ClientID("cf482fac-3a0b-11ed-a261-0242ac120002")} cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) _require.Nil(err) @@ -653,73 +653,3 @@ func (s *ServiceRecordedTestsSuite) TestUDKServiceClient() { _, err = sc.DeleteContainer(context.Background(), containerName+"002", nil) _require.Nil(err) } - -/*func (s *ServiceUnrecordedTestsSuite) TestUDKContainerClient() { - _require := require.New(s.T()) - testName := s.T().Name() - accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") - accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY") - cred, err := azblob.NewSharedKeyCredential(accountName, accountKey) - _require.Nil(err) - - serviceClient, err := service.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, nil) - _require.Nil(err) - - containerName := testcommon.GenerateContainerName(testName) - containerClient := serviceClient.NewContainerClient(containerName) - - permissions := container.SASPermissions{ - Read: true, - Add: true, - } - start := time.Now().Add(-5 * time.Minute).UTC() - expiry := time.Now().Add(time.Hour) - - sasUrl, err := containerClient.GetSASURL(permissions, start, expiry) - _require.Nil(err) - - containerClient2, err := container.NewClientWithNoCredential(sasUrl, nil) - _require.Nil(err) - - _, err = containerClient2.Create(context.Background(), &container.CreateOptions{Metadata: testcommon.BasicMetadata}) - _require.NotNil(err) - testcommon.ValidateBlobErrorCode(_require, err, bloberror.AuthorizationFailure) -} - -func (s *ServiceUnrecordedTestsSuite) TestUDKContainerClient2() { - _require := require.New(s.T()) - testName := s.T().Name() - accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") - accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY") - cred, err := azblob.NewSharedKeyCredential(accountName, accountKey) - _require.Nil(err) - - serviceClient, err := service.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, nil) - _require.Nil(err) - - containerName := testcommon.GenerateContainerName(testName) - containerClient := serviceClient.NewContainerClient(containerName) - - sasUrlReadAdd, err := containerClient.GetSASURL(container.SASPermissions{Read: true, Add: true}, - time.Now().Add(-5*time.Minute).UTC(), time.Now().Add(time.Hour)) - _require.Nil(err) - _, err = containerClient.Create(context.Background(), &container.CreateOptions{Metadata: testcommon.BasicMetadata}) - _require.Nil(err) - - containerClient1, err := container.NewClientWithNoCredential(sasUrlReadAdd, nil) - _require.Nil(err) - - _, err = containerClient1.GetProperties(context.Background(), nil) - _require.Nil(err) - //validateBlobErrorCode(_require, err, bloberror.AuthorizationFailure) - // - //sasUrlRCWL, err := containerClient.GetSASURL(container.SASPermissions{Add: true, Create: true, Delete: true, List: true}, - // time.Now().Add(-5*time.Minute).UTC(), time.Now().Add(time.Hour)) - //_require.Nil(err) - // - //containerClient2, err := container.NewClientWithNoCredential(sasUrlRCWL, nil) - //_require.Nil(err) - // - //_, err = containerClient2.Create(ctx, nil) - //_require.Nil(err) -}*/ From 4f6ad3f945f393c7744335cb17a2c04329b0a8aa Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:34:00 -0700 Subject: [PATCH 07/22] Clean up --- sdk/storage/azblob/common.go | 8 ++++---- sdk/storage/azblob/service/models.go | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sdk/storage/azblob/common.go b/sdk/storage/azblob/common.go index dcc102544f22..f8e1aeb5140e 100644 --- a/sdk/storage/azblob/common.go +++ b/sdk/storage/azblob/common.go @@ -24,14 +24,14 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia // UserDelegationCredential contains an account's name and its user delegation key. type UserDelegationCredential = exported.UserDelegationCredential +// UserDelegationKey contains UserDelegationKey. +type UserDelegationKey = generated.UserDelegationKey + // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { +func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { return exported.NewUserDelegationCredential(accountName, udk) } -// UserDelegationKey contains UserDelegationKey. -type UserDelegationKey = generated.UserDelegationKey - // URLParts object represents the components that make up an Azure Storage Container/Blob URL. // NOTE: Changing any SAS-related field requires computing a new SAS signature. type URLParts = sas.URLParts diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index 23b8f5aa363d..2eebcbfc867d 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -24,11 +24,15 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia // UserDelegationCredential contains an account's name and its user delegation key. type UserDelegationCredential = exported.UserDelegationCredential +// UserDelegationKey contains UserDelegationKey. +type UserDelegationKey = generated.UserDelegationKey + // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { +func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { return exported.NewUserDelegationCredential(accountName, udk) } +// GetUserDelegationKeyOptions contains request id and timeout type GetUserDelegationKeyOptions = generated.ServiceClientGetUserDelegationKeyOptions // AccessConditions identifies container-specific access conditions which you optionally set. From aef324fd24d4a9b8beff45d4e1b2673e939c7558 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 10:19:08 -0700 Subject: [PATCH 08/22] Addressing comments in PR --- sdk/storage/azblob/blob/models.go | 3 --- sdk/storage/azblob/container/models.go | 3 --- .../exported/user_delegation_credential.go | 2 +- sdk/storage/azblob/sas/service.go | 14 ++++++-------- sdk/storage/azblob/service/client.go | 2 +- sdk/storage/azblob/service/models.go | 2 ++ 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/sdk/storage/azblob/blob/models.go b/sdk/storage/azblob/blob/models.go index ebdc025a470f..bc597049ffe1 100644 --- a/sdk/storage/azblob/blob/models.go +++ b/sdk/storage/azblob/blob/models.go @@ -23,9 +23,6 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } -// UserDelegationKey contains UserDelegationKey. -type UserDelegationKey = generated.UserDelegationKey - // Type Declarations --------------------------------------------------------------------- // AccessConditions identifies blob-specific access conditions which you optionally set. diff --git a/sdk/storage/azblob/container/models.go b/sdk/storage/azblob/container/models.go index f1bbdabfc691..971a10a3dbef 100644 --- a/sdk/storage/azblob/container/models.go +++ b/sdk/storage/azblob/container/models.go @@ -22,9 +22,6 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } -// UserDelegationKey contains UserDelegationKey. -type UserDelegationKey = exported.UserDelegationKey - // Request Model Declaration ------------------------------------------------------------------------------------------- // CpkScopeInfo contains a group of parameters for the ContainerClient.Create method. diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index d9d8205106f9..bdd2cdccd4f6 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -14,7 +14,7 @@ import ( ) // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's Name and a user delegation Key from it -func NewUserDelegationCredential(accountName string, udk generated.UserDelegationKey) *UserDelegationCredential { +func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { return &UserDelegationCredential{ Name: accountName, Key: udk, diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index 87b0187cd82a..bcc53f2cbd36 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -236,14 +236,12 @@ func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegatio } //User delegation SAS specific parameters - if udk != nil { - p.signedOID = *udk.SignedOID - p.signedTID = *udk.SignedTID - p.signedStart = *udk.SignedStart - p.signedExpiry = *udk.SignedExpiry - p.signedService = *udk.SignedService - p.signedVersion = *udk.SignedVersion - } + p.signedOID = *udk.SignedOID + p.signedTID = *udk.SignedTID + p.signedStart = *udk.SignedStart + p.signedExpiry = *udk.SignedExpiry + p.signedService = *udk.SignedService + p.signedVersion = *udk.SignedVersion return p, nil } diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index e2a03406d36a..7c76104f3ad8 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -104,7 +104,7 @@ func GetUserDelegationCredential(serviceURL string, ctx context.Context, info ge return NewUserDelegationCredential(url.Host, udk.UserDelegationKey), nil } -func (s Client) GetUserDelegationKey(ctx context.Context, keyInfo generated.KeyInfo, options *GetUserDelegationKeyOptions) GetUserDelegationKeyResponse { +func (s Client) GetUserDelegationKey(ctx context.Context, keyInfo KeyInfo, options *GetUserDelegationKeyOptions) GetUserDelegationKeyResponse { resp, _ := s.generated().GetUserDelegationKey(ctx, keyInfo, options) return resp } diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index 2eebcbfc867d..36c4f90fdc79 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -27,6 +27,8 @@ type UserDelegationCredential = exported.UserDelegationCredential // UserDelegationKey contains UserDelegationKey. type UserDelegationKey = generated.UserDelegationKey +type KeyInfo = generated.KeyInfo + // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { return exported.NewUserDelegationCredential(accountName, udk) From 632381df6edc3cba57a2c622052dea612cc3572a Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 10:32:45 -0700 Subject: [PATCH 09/22] Adding helper methods to only allow SDK calls --- .../exported/user_delegation_credential.go | 16 +++++++++++++--- sdk/storage/azblob/sas/account.go | 18 ++++++++---------- sdk/storage/azblob/sas/service.go | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index bdd2cdccd4f6..0600ac663c25 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -35,8 +35,8 @@ func (f *UserDelegationCredential) AccountName() string { return f.Name } -// ComputeHMAC -func (f *UserDelegationCredential) ComputeHMACSHA256(message string) (string, error) { +// computeHMACSHA256 generates a hash signature for an HTTP request or for a SAS. +func (f *UserDelegationCredential) computeHMACSHA256(message string) (string, error) { bytes, _ := base64.StdEncoding.DecodeString(*f.Key.Value) h := hmac.New(sha256.New, bytes) _, err := h.Write([]byte(message)) @@ -44,6 +44,16 @@ func (f *UserDelegationCredential) ComputeHMACSHA256(message string) (string, er } // GetUDKParams returns UserDelegationKey -func (f *UserDelegationCredential) GetUDKParams() *generated.UserDelegationKey { +func (f *UserDelegationCredential) getUDKParams() *UserDelegationKey { return &f.Key } + +// ComputeUDCHMACSHA256 is a helper method for computing the signed string outside of this package. +func ComputeUDCHMACSHA256(udc *UserDelegationCredential, message string) (string, error) { + return udc.computeHMACSHA256(message) +} + +// GetUDKParams is a helper method for accessing the user delegation key parameters outside of this package. +func GetUDKParams(udc *UserDelegationCredential) *UserDelegationKey { + return udc.getUDKParams() +} diff --git a/sdk/storage/azblob/sas/account.go b/sdk/storage/azblob/sas/account.go index a2ff7f536fd8..82c82b2cb651 100644 --- a/sdk/storage/azblob/sas/account.go +++ b/sdk/storage/azblob/sas/account.go @@ -121,7 +121,7 @@ func (v AccountSignatureValues) SignWithUDK(userDelegationCredential *UserDelega ""}, // That is right, the account SAS requires a terminating extra newline "\n") - signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) + signature, err := exported.ComputeUDCHMACSHA256(userDelegationCredential, stringToSign) if err != nil { return QueryParameters{}, err } @@ -142,17 +142,15 @@ func (v AccountSignatureValues) SignWithUDK(userDelegationCredential *UserDelega signature: signature, } - udk := userDelegationCredential.GetUDKParams() + udk := exported.GetUDKParams(userDelegationCredential) //User delegation SAS specific parameters - if udk != nil { - p.signedOID = *udk.SignedOID - p.signedTID = *udk.SignedTID - p.signedStart = *udk.SignedStart - p.signedExpiry = *udk.SignedExpiry - p.signedService = *udk.SignedService - p.signedVersion = *udk.SignedVersion - } + p.signedOID = *udk.SignedOID + p.signedTID = *udk.SignedTID + p.signedStart = *udk.SignedStart + p.signedExpiry = *udk.SignedExpiry + p.signedService = *udk.SignedService + p.signedVersion = *udk.SignedVersion return p, nil } diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index bcc53f2cbd36..a10fd73deb0a 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -168,7 +168,7 @@ func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegatio signedIdentifier := v.Identifier - udk := userDelegationCredential.GetUDKParams() + udk := exported.GetUDKParams(userDelegationCredential) udkStart, udkExpiry, _ := formatTimesForSigning(*udk.SignedStart, *udk.SignedExpiry, time.Time{}) //I don't like this answer to combining the functions @@ -204,7 +204,7 @@ func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegatio v.ContentType}, // rsct "\n") - signature, err := userDelegationCredential.ComputeHMACSHA256(stringToSign) + signature, err := exported.ComputeUDCHMACSHA256(userDelegationCredential, stringToSign) if err != nil { return QueryParameters{}, err } From a35f7a6f9492fe7e70bbd77ab674fea717b8b71a Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 10:51:14 -0700 Subject: [PATCH 10/22] Adding to example_test and making udc immutable --- .../exported/user_delegation_credential.go | 14 +++--- sdk/storage/azblob/service/examples_test.go | 50 +++++++++++++++++-- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index 0600ac663c25..b4195fccbedb 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -16,8 +16,8 @@ import ( // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's Name and a user delegation Key from it func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { return &UserDelegationCredential{ - Name: accountName, - Key: udk, + accountName: accountName, + userDelegationKey: udk, } } @@ -26,18 +26,18 @@ type UserDelegationKey = generated.UserDelegationKey // UserDelegationCredential contains an account's name and its user delegation key. type UserDelegationCredential struct { - Name string - Key UserDelegationKey + accountName string + userDelegationKey UserDelegationKey } // AccountName returns the Storage account's Name func (f *UserDelegationCredential) AccountName() string { - return f.Name + return f.accountName } // computeHMACSHA256 generates a hash signature for an HTTP request or for a SAS. func (f *UserDelegationCredential) computeHMACSHA256(message string) (string, error) { - bytes, _ := base64.StdEncoding.DecodeString(*f.Key.Value) + bytes, _ := base64.StdEncoding.DecodeString(*f.userDelegationKey.Value) h := hmac.New(sha256.New, bytes) _, err := h.Write([]byte(message)) return base64.StdEncoding.EncodeToString(h.Sum(nil)), err @@ -45,7 +45,7 @@ func (f *UserDelegationCredential) computeHMACSHA256(message string) (string, er // GetUDKParams returns UserDelegationKey func (f *UserDelegationCredential) getUDKParams() *UserDelegationKey { - return &f.Key + return &f.userDelegationKey } // ComputeUDCHMACSHA256 is a helper method for computing the signed string outside of this package. diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index 4a5e6dbf0175..30bf17744e8a 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -273,11 +273,32 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - _, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) if err != nil { log.Fatal(err) } - fmt.Println("User Delegation Key has been created for ", accountName) + + fmt.Println("User Delegation Key has been created for ", udc.AccountName()) + + // Create Account Signature Values with desired permissions and sign with user delegation credential + sasQueryParams, err := sas.AccountSignatureValues{ + Protocol: sas.ProtocolHTTPS, + ExpiryTime: time.Now().UTC().Add(48 * time.Hour), + Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), + Services: to.Ptr(sas.AccountServices{Blob: true}).String(), + ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), + }.SignWithUDK(udc) + handleError(err) + + sasURL := fmt.Sprintf("https://%s.blob.core.windows.net/?%s", accountName, sasQueryParams.Encode()) + + // This URL can be used to authenticate requests now + serviceClient, err := service.NewClientWithNoCredential(sasURL, nil) + handleError(err) + + // You can also break a blob URL up into it's constituent parts + blobURLParts, _ := blob.ParseURL(serviceClient.URL()) + fmt.Printf("SAS expiry time = %s\n", blobURLParts.SAS.ExpiryTime()) // Create Managed Identity (OAuth) Credentials using Resource ID optsResourceID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ResourceID("/subscriptions/...")} @@ -288,9 +309,30 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) - _, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + udc, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) if err != nil { log.Fatal(err) } - fmt.Println("User Delegation Key has been created for ", accountName) + fmt.Println("User Delegation Key has been created for ", udc.AccountName()) + + // Create Account Signature Values with desired permissions and sign with user delegation credential + sasQueryParams, err = sas.AccountSignatureValues{ + Protocol: sas.ProtocolHTTPS, + ExpiryTime: time.Now().UTC().Add(48 * time.Hour), + Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), + Services: to.Ptr(sas.AccountServices{Blob: true}).String(), + ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), + }.SignWithUDK(udc) + handleError(err) + + sasURL = fmt.Sprintf("https://%s.blob.core.windows.net/?%s", accountName, sasQueryParams.Encode()) + + // This URL can be used to authenticate requests now + serviceClient, err = service.NewClientWithNoCredential(sasURL, nil) + handleError(err) + + // You can also break a blob URL up into it's constituent parts + blobURLParts, _ = blob.ParseURL(serviceClient.URL()) + fmt.Printf("SAS expiry time = %s\n", blobURLParts.SAS.ExpiryTime()) + } From a9a1508d821f81f8fe8aade7bda41c4894af0c84 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 14:01:18 -0700 Subject: [PATCH 11/22] Adding helper method for GetAccountName --- .../exported/user_delegation_credential.go | 17 +++++++++++------ sdk/storage/azblob/sas/account.go | 2 +- sdk/storage/azblob/sas/service.go | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/sdk/storage/azblob/internal/exported/user_delegation_credential.go b/sdk/storage/azblob/internal/exported/user_delegation_credential.go index b4195fccbedb..b0aca86eea63 100644 --- a/sdk/storage/azblob/internal/exported/user_delegation_credential.go +++ b/sdk/storage/azblob/internal/exported/user_delegation_credential.go @@ -31,10 +31,15 @@ type UserDelegationCredential struct { } // AccountName returns the Storage account's Name -func (f *UserDelegationCredential) AccountName() string { +func (f *UserDelegationCredential) getAccountName() string { return f.accountName } +// GetUDKParams is a helper method for accessing the user delegation key parameters outside of this package. +func GetAccountName(udc *UserDelegationCredential) string { + return udc.getAccountName() +} + // computeHMACSHA256 generates a hash signature for an HTTP request or for a SAS. func (f *UserDelegationCredential) computeHMACSHA256(message string) (string, error) { bytes, _ := base64.StdEncoding.DecodeString(*f.userDelegationKey.Value) @@ -43,16 +48,16 @@ func (f *UserDelegationCredential) computeHMACSHA256(message string) (string, er return base64.StdEncoding.EncodeToString(h.Sum(nil)), err } -// GetUDKParams returns UserDelegationKey -func (f *UserDelegationCredential) getUDKParams() *UserDelegationKey { - return &f.userDelegationKey -} - // ComputeUDCHMACSHA256 is a helper method for computing the signed string outside of this package. func ComputeUDCHMACSHA256(udc *UserDelegationCredential, message string) (string, error) { return udc.computeHMACSHA256(message) } +// GetUDKParams returns UserDelegationKey +func (f *UserDelegationCredential) getUDKParams() *UserDelegationKey { + return &f.userDelegationKey +} + // GetUDKParams is a helper method for accessing the user delegation key parameters outside of this package. func GetUDKParams(udc *UserDelegationCredential) *UserDelegationKey { return udc.getUDKParams() diff --git a/sdk/storage/azblob/sas/account.go b/sdk/storage/azblob/sas/account.go index 82c82b2cb651..7898047a4108 100644 --- a/sdk/storage/azblob/sas/account.go +++ b/sdk/storage/azblob/sas/account.go @@ -109,7 +109,7 @@ func (v AccountSignatureValues) SignWithUDK(userDelegationCredential *UserDelega startTime, expiryTime, _ := formatTimesForSigning(v.StartTime, v.ExpiryTime, time.Time{}) stringToSign := strings.Join([]string{ - userDelegationCredential.AccountName(), + exported.GetAccountName(userDelegationCredential), v.Permissions, v.Services, v.ResourceTypes, diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index a10fd73deb0a..6727f4a159c0 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -190,7 +190,7 @@ func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegatio v.Permissions, startTime, expiryTime, - getCanonicalName(userDelegationCredential.AccountName(), v.ContainerName, v.BlobName, v.Directory), + getCanonicalName(exported.GetAccountName(userDelegationCredential), v.ContainerName, v.BlobName, v.Directory), signedIdentifier, v.IPRange.String(), string(v.Protocol), From 95f0746b2861f0af2ed00ae3f5abfdd5b32a74b0 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 15:24:58 -0700 Subject: [PATCH 12/22] Additional clean up --- sdk/storage/azblob/client.go | 4 ++++ sdk/storage/azblob/common.go | 12 ------------ sdk/storage/azblob/service/client.go | 21 ++++++--------------- sdk/storage/azblob/service/client_test.go | 2 +- sdk/storage/azblob/service/examples_test.go | 8 ++++---- sdk/storage/azblob/service/models.go | 10 ++++++++-- 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index c3cd9d02925a..8522fda971c2 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -158,3 +158,7 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN o = shared.CopyOptions(o) return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } + +func (c *Client) GetUserDelegationCredential(background context.Context, info service.KeyInfo, opts *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { + return c.svc.GetUserDelegationCredential(background, info, opts) +} diff --git a/sdk/storage/azblob/common.go b/sdk/storage/azblob/common.go index f8e1aeb5140e..560e151d553f 100644 --- a/sdk/storage/azblob/common.go +++ b/sdk/storage/azblob/common.go @@ -8,7 +8,6 @@ package azblob import ( "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported" - "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" ) @@ -21,17 +20,6 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia return exported.NewSharedKeyCredential(accountName, accountKey) } -// UserDelegationCredential contains an account's name and its user delegation key. -type UserDelegationCredential = exported.UserDelegationCredential - -// UserDelegationKey contains UserDelegationKey. -type UserDelegationKey = generated.UserDelegationKey - -// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { - return exported.NewUserDelegationCredential(accountName, udk) -} - // URLParts object represents the components that make up an Azure Storage Container/Blob URL. // NOTE: Changing any SAS-related field requires computing a new SAS signature. type URLParts = sas.URLParts diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index 7c76104f3ad8..48aa571290ce 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,28 +85,19 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func GetUserDelegationCredential(serviceURL string, ctx context.Context, info generated.KeyInfo, timeout *int32, requestID *string) (*UserDelegationCredential, error) { - url, err := blob.ParseURL(serviceURL) +func (s *Client) GetUserDelegationCredential(ctx context.Context, info generated.KeyInfo, o *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { + url, err := blob.ParseURL(s.URL()) if err != nil { return nil, err } - pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, nil) - sc := generated.NewServiceClient(serviceURL, pl) - opts := GetUserDelegationKeyOptions{ - RequestID: requestID, - Timeout: timeout, - } - udk, err := sc.GetUserDelegationKey(ctx, info, &opts) + + getUserDelegationKeyOptions := o.format() + udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationKeyOptions) if err != nil { return nil, err } - return NewUserDelegationCredential(url.Host, udk.UserDelegationKey), nil -} - -func (s Client) GetUserDelegationKey(ctx context.Context, keyInfo KeyInfo, options *GetUserDelegationKeyOptions) GetUserDelegationKeyResponse { - resp, _ := s.generated().GetUserDelegationKey(ctx, keyInfo, options) - return resp + return NewUserDelegationCredential(strings.Split(url.Host, ".")[0], udk.UserDelegationKey), nil } func (s *Client) generated() *generated.ServiceClient { diff --git a/sdk/storage/azblob/service/client_test.go b/sdk/storage/azblob/service/client_test.go index 940ea8df7ffc..875d3e1d5295 100644 --- a/sdk/storage/azblob/service/client_test.go +++ b/sdk/storage/azblob/service/client_test.go @@ -625,7 +625,7 @@ func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { } // Get UserDelegationCredential - udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + udc, err := svcClient.GetUserDelegationCredential(context.Background(), info, nil) _require.Nil(err) csas, err := sas.AccountSignatureValues{ diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index 30bf17744e8a..f6d6d453939a 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -273,12 +273,12 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + udc, err := svcClient.GetUserDelegationCredential(context.Background(), info, nil) if err != nil { log.Fatal(err) } - fmt.Println("User Delegation Key has been created for ", udc.AccountName()) + fmt.Println("User Delegation Key has been created for ", accountName) // Create Account Signature Values with desired permissions and sign with user delegation credential sasQueryParams, err := sas.AccountSignatureValues{ @@ -309,11 +309,11 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) - udc, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil, nil) + udc, err = svcClient.GetUserDelegationCredential(context.Background(), info, nil) if err != nil { log.Fatal(err) } - fmt.Println("User Delegation Key has been created for ", udc.AccountName()) + fmt.Println("User Delegation Key has been created for ", accountName) // Create Account Signature Values with desired permissions and sign with user delegation credential sasQueryParams, err = sas.AccountSignatureValues{ diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index 36c4f90fdc79..7c97438fb9ec 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -34,8 +34,14 @@ func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *Use return exported.NewUserDelegationCredential(accountName, udk) } -// GetUserDelegationKeyOptions contains request id and timeout -type GetUserDelegationKeyOptions = generated.ServiceClientGetUserDelegationKeyOptions +// GetUserDelegationKeyOptions contains optional parameters for Service.GetUserDelegationKey method +type GetUserDelegationKeyOptions struct { + // placeholder for future options +} + +func (o *GetUserDelegationKeyOptions) format() *generated.ServiceClientGetUserDelegationKeyOptions { + return nil +} // AccessConditions identifies container-specific access conditions which you optionally set. type AccessConditions = exported.ContainerAccessConditions From 46287c7dd17cd029e701c549526a9ecafa1c7eb0 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:04:15 -0700 Subject: [PATCH 13/22] Minor changes & commenting out test --- sdk/storage/azblob/client.go | 4 ++-- sdk/storage/azblob/sas/account.go | 4 ++-- sdk/storage/azblob/sas/service.go | 4 ++-- sdk/storage/azblob/service/client.go | 2 +- sdk/storage/azblob/service/client_test.go | 10 +++------- sdk/storage/azblob/service/examples_test.go | 4 ++-- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index 8522fda971c2..91bf46565599 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -159,6 +159,6 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } -func (c *Client) GetUserDelegationCredential(background context.Context, info service.KeyInfo, opts *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { - return c.svc.GetUserDelegationCredential(background, info, opts) +func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { + return c.svc.GetUserDelegationCredential(ctx, info, o) } diff --git a/sdk/storage/azblob/sas/account.go b/sdk/storage/azblob/sas/account.go index 7898047a4108..fa76ed95c416 100644 --- a/sdk/storage/azblob/sas/account.go +++ b/sdk/storage/azblob/sas/account.go @@ -90,8 +90,8 @@ func (v AccountSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKey return p, nil } -// SignWithUDK uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. -func (v AccountSignatureValues) SignWithUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { +// SignWithUserDelegation uses an account's UserDelegationKey to sign this signature values to produce the proper SAS query parameters. +func (v AccountSignatureValues) SignWithUserDelegation(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" { return QueryParameters{}, errors.New("account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType") diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index 6727f4a159c0..fa779042cb59 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -134,8 +134,8 @@ func (v BlobSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKeyCre return p, nil } -// SignWithUDK uses an account's UserDelegationCredential to sign this signature values to produce the proper SAS query parameters. -func (v BlobSignatureValues) SignWithUDK(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { +// SignWithUserDelegation uses an account's UserDelegationCredential to sign this signature values to produce the proper SAS query parameters. +func (v BlobSignatureValues) SignWithUserDelegation(userDelegationCredential *UserDelegationCredential) (QueryParameters, error) { if userDelegationCredential == nil { return QueryParameters{}, fmt.Errorf("cannot sign SAS query without User Delegation Key") } diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index 0b6764306a29..69c4b2598063 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,7 +85,7 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func (s *Client) GetUserDelegationCredential(ctx context.Context, info generated.KeyInfo, o *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { +func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, o *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { url, err := blob.ParseURL(s.URL()) if err != nil { return nil, err diff --git a/sdk/storage/azblob/service/client_test.go b/sdk/storage/azblob/service/client_test.go index 111dc54d31d0..491dd3048478 100644 --- a/sdk/storage/azblob/service/client_test.go +++ b/sdk/storage/azblob/service/client_test.go @@ -9,10 +9,7 @@ package service_test import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated" "os" - "strings" "testing" "time" @@ -603,8 +600,7 @@ func (s *ServiceUnrecordedTestsSuite) TestSASContainerClient2() { //_require.Nil(err) } - -func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { +/*func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { _require := require.New(s.T()) testName := s.T().Name() accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME") @@ -635,7 +631,7 @@ func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.SignWithUDK(udc) + }.SignWithUserDelegation(udc) _require.Nil(err) sasURL := svcClient.URL() @@ -653,7 +649,7 @@ func (s *ServiceRecordedTestsSuite) TestUserDelegationSAS() { _, err = sc.DeleteContainer(context.Background(), containerName+"002", nil) _require.Nil(err) -} +}*/ // make sure that container soft delete is enabled // TODO: convert this test to recorded diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index a383725b5798..e9232c08229e 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -321,7 +321,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.SignWithUDK(udc) + }.SignWithUserDelegation(udc) handleError(err) sasURL := fmt.Sprintf("https://%s.blob.core.windows.net/?%s", accountName, sasQueryParams.Encode()) @@ -356,7 +356,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Permissions: to.Ptr(sas.AccountPermissions{Read: true, List: true}).String(), Services: to.Ptr(sas.AccountServices{Blob: true}).String(), ResourceTypes: to.Ptr(sas.AccountResourceTypes{Container: true, Object: true}).String(), - }.SignWithUDK(udc) + }.SignWithUserDelegation(udc) handleError(err) sasURL = fmt.Sprintf("https://%s.blob.core.windows.net/?%s", accountName, sasQueryParams.Encode()) From 17310e5091ca59be6c5fe8517aaac719b964f585 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:07:31 -0700 Subject: [PATCH 14/22] Renaming of opts --- sdk/storage/azblob/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index 91bf46565599..fff32762f301 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -159,6 +159,6 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } -func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { - return c.svc.GetUserDelegationCredential(ctx, info, o) +func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, options *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { + return c.svc.GetUserDelegationCredential(ctx, info, options) } From cdd17c58587083065c293fbbf97b9c82f7cadb77 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:11:46 -0700 Subject: [PATCH 15/22] Changing option name for GetUserDelegationCredential --- sdk/storage/azblob/client.go | 4 ++-- sdk/storage/azblob/service/client.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index fff32762f301..91bf46565599 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -159,6 +159,6 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } -func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, options *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { - return c.svc.GetUserDelegationCredential(ctx, info, options) +func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { + return c.svc.GetUserDelegationCredential(ctx, info, o) } diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index 69c4b2598063..e3d95d69cad5 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,13 +85,13 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, o *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { +func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, userDelegationKeyOptions *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { url, err := blob.ParseURL(s.URL()) if err != nil { return nil, err } - getUserDelegationKeyOptions := o.format() + getUserDelegationKeyOptions := userDelegationKeyOptions.format() udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationKeyOptions) if err != nil { return nil, err From 73bb43945cdfe7789c882f1fe191be2d7e6c9d31 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:19:00 -0700 Subject: [PATCH 16/22] Minor type renaming --- sdk/storage/azblob/client.go | 2 +- sdk/storage/azblob/service/client.go | 6 +++--- sdk/storage/azblob/service/models.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index 91bf46565599..5254ec5262a0 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -159,6 +159,6 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } -func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationKeyOptions) (*service.UserDelegationCredential, error) { +func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationCredentialOptions) (*service.UserDelegationCredential, error) { return c.svc.GetUserDelegationCredential(ctx, info, o) } diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index e3d95d69cad5..d2a63f11b214 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,14 +85,14 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, userDelegationKeyOptions *GetUserDelegationKeyOptions) (*UserDelegationCredential, error) { +func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, o *GetUserDelegationCredentialOptions) (*UserDelegationCredential, error) { url, err := blob.ParseURL(s.URL()) if err != nil { return nil, err } - getUserDelegationKeyOptions := userDelegationKeyOptions.format() - udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationKeyOptions) + getUserDelegationCredentialOptions := o.format() + udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationCredentialOptions) if err != nil { return nil, err } diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index 33c42b22aa5e..7d8111eb4a92 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -34,12 +34,12 @@ func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *Use return exported.NewUserDelegationCredential(accountName, udk) } -// GetUserDelegationKeyOptions contains optional parameters for Service.GetUserDelegationKey method -type GetUserDelegationKeyOptions struct { +// GetUserDelegationCredentialOptions contains optional parameters for Service.GetUserDelegationKey method +type GetUserDelegationCredentialOptions struct { // placeholder for future options } -func (o *GetUserDelegationKeyOptions) format() *generated.ServiceClientGetUserDelegationKeyOptions { +func (o *GetUserDelegationCredentialOptions) format() *generated.ServiceClientGetUserDelegationKeyOptions { return nil } From 1c8a6c3b69e59fc5f5053ac6ea32d882c929d8a9 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:29:34 -0700 Subject: [PATCH 17/22] Putting GetUserDelegationCredential on service.client --- sdk/storage/azblob/client.go | 4 ---- sdk/storage/azblob/service/client.go | 8 +++++--- sdk/storage/azblob/service/examples_test.go | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/sdk/storage/azblob/client.go b/sdk/storage/azblob/client.go index 5254ec5262a0..c3cd9d02925a 100644 --- a/sdk/storage/azblob/client.go +++ b/sdk/storage/azblob/client.go @@ -158,7 +158,3 @@ func (c *Client) DownloadStream(ctx context.Context, containerName string, blobN o = shared.CopyOptions(o) return c.svc.NewContainerClient(containerName).NewBlobClient(blobName).DownloadStream(ctx, o) } - -func (c *Client) GetUserDelegationCredential(ctx context.Context, info service.KeyInfo, o *service.GetUserDelegationCredentialOptions) (*service.UserDelegationCredential, error) { - return c.svc.GetUserDelegationCredential(ctx, info, o) -} diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index d2a63f11b214..b1dc39eae5a2 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,14 +85,16 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, o *GetUserDelegationCredentialOptions) (*UserDelegationCredential, error) { - url, err := blob.ParseURL(s.URL()) +func GetUserDelegationCredential(serviceURL string, ctx context.Context, info KeyInfo, o *GetUserDelegationCredentialOptions) (*UserDelegationCredential, error) { + url, err := blob.ParseURL(serviceURL) if err != nil { return nil, err } + pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, nil) + sc := generated.NewServiceClient(serviceURL, pl) getUserDelegationCredentialOptions := o.format() - udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationCredentialOptions) + udk, err := sc.GetUserDelegationKey(ctx, info, getUserDelegationCredentialOptions) if err != nil { return nil, err } diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index e9232c08229e..1352aa30e82f 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -307,7 +307,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - udc, err := svcClient.GetUserDelegationCredential(context.Background(), info, nil) + udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) if err != nil { log.Fatal(err) } @@ -343,7 +343,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) - udc, err = svcClient.GetUserDelegationCredential(context.Background(), info, nil) + udc, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) if err != nil { log.Fatal(err) } From b8d57dd57ebd468fa70344e4e1ef266188b09ed1 Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:31:05 -0700 Subject: [PATCH 18/22] Adding doc comment --- sdk/storage/azblob/service/models.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index 7d8111eb4a92..e6a26395824c 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -27,6 +27,7 @@ type UserDelegationCredential = exported.UserDelegationCredential // UserDelegationKey contains UserDelegationKey. type UserDelegationKey = generated.UserDelegationKey +// KeyInfo contains KeyInfo struct with expiry and start strings. type KeyInfo = generated.KeyInfo // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it From f1d110a787c16ca8100dd3f227803de731e39a7e Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:35:31 -0700 Subject: [PATCH 19/22] Updating doc comment for KeyInfo --- sdk/storage/azblob/service/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index e6a26395824c..a65356b04dff 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -27,7 +27,7 @@ type UserDelegationCredential = exported.UserDelegationCredential // UserDelegationKey contains UserDelegationKey. type UserDelegationKey = generated.UserDelegationKey -// KeyInfo contains KeyInfo struct with expiry and start strings. +// KeyInfo contains KeyInfo struct. type KeyInfo = generated.KeyInfo // NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it From 0a046e5f42cd73d39fc5f29aed288812749ce17d Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Fri, 23 Sep 2022 10:38:59 -0700 Subject: [PATCH 20/22] Fixing example test with handle error --- sdk/storage/azblob/service/examples_test.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index 1352aa30e82f..3f29f79858b1 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -289,15 +289,14 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { panic("AZURE_STORAGE_ACCOUNT_NAME could not be found") } // Create Managed Identity (OAuth) Credentials using Client ID - clientOptions := azcore.ClientOptions{} + clientOptions := azcore.ClientOptions{} // Fill clientOptions as needed optsClientID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ClientID("7cf7db0d-...")} cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) - if err != nil { - log.Fatal(err) - } + handleError(err) clientOptionsAzBlob := azblob.ClientOptions{} // Same as azcore.ClientOptions using azblob instead svcClient, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &clientOptionsAzBlob) + handleError(err) // Set current and past time and create key currentTime := time.Now().UTC().Add(-10 * time.Second) @@ -308,9 +307,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { } udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) - if err != nil { - log.Fatal(err) - } + handleError(err) fmt.Println("User Delegation Key has been created for ", accountName) @@ -337,16 +334,13 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { // Create Managed Identity (OAuth) Credentials using Resource ID optsResourceID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ResourceID("/subscriptions/...")} cred, err = azidentity.NewManagedIdentityCredential(&optsResourceID) - if err != nil { - log.Fatal(err) - } + handleError(err) svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) + handleError(err) udc, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) - if err != nil { - log.Fatal(err) - } + handleError(err) fmt.Println("User Delegation Key has been created for ", accountName) // Create Account Signature Values with desired permissions and sign with user delegation credential From 07c039f571d31da509f3899420de6de5d29e674a Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Fri, 23 Sep 2022 11:10:18 -0700 Subject: [PATCH 21/22] Fixing GetUserDelegationCredential and examples - Service.Client --- sdk/storage/azblob/sas/service.go | 4 +--- sdk/storage/azblob/service/client.go | 10 ++++------ sdk/storage/azblob/service/examples_test.go | 10 +++++----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/sdk/storage/azblob/sas/service.go b/sdk/storage/azblob/sas/service.go index fa779042cb59..98d853d4e9f8 100644 --- a/sdk/storage/azblob/sas/service.go +++ b/sdk/storage/azblob/sas/service.go @@ -166,14 +166,12 @@ func (v BlobSignatureValues) SignWithUserDelegation(userDelegationCredential *Us } startTime, expiryTime, snapshotTime := formatTimesForSigning(v.StartTime, v.ExpiryTime, v.SnapshotTime) - signedIdentifier := v.Identifier - udk := exported.GetUDKParams(userDelegationCredential) udkStart, udkExpiry, _ := formatTimesForSigning(*udk.SignedStart, *udk.SignedExpiry, time.Time{}) //I don't like this answer to combining the functions //But because signedIdentifier and the user delegation key strings share a place, this is an _OK_ way to do it. - signedIdentifier = strings.Join([]string{ + signedIdentifier := strings.Join([]string{ *udk.SignedOID, *udk.SignedTID, udkStart, diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index b1dc39eae5a2..b3c9288ec203 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -85,16 +85,14 @@ func NewClientFromConnectionString(connectionString string, options *ClientOptio // GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. // OAuth is required for this call, as well as any role that can delegate access to the storage account. -func GetUserDelegationCredential(serviceURL string, ctx context.Context, info KeyInfo, o *GetUserDelegationCredentialOptions) (*UserDelegationCredential, error) { - url, err := blob.ParseURL(serviceURL) +func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, o *GetUserDelegationCredentialOptions) (*UserDelegationCredential, error) { + url, err := blob.ParseURL(s.URL()) if err != nil { return nil, err } - pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, nil) - sc := generated.NewServiceClient(serviceURL, pl) - getUserDelegationCredentialOptions := o.format() - udk, err := sc.GetUserDelegationKey(ctx, info, getUserDelegationCredentialOptions) + getUserDelegationKeyOptions := o.format() + udk, err := s.generated().GetUserDelegationKey(ctx, info, getUserDelegationKeyOptions) if err != nil { return nil, err } diff --git a/sdk/storage/azblob/service/examples_test.go b/sdk/storage/azblob/service/examples_test.go index 3f29f79858b1..e7ce4250555e 100644 --- a/sdk/storage/azblob/service/examples_test.go +++ b/sdk/storage/azblob/service/examples_test.go @@ -293,9 +293,9 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { optsClientID := azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOptions, ID: azidentity.ClientID("7cf7db0d-...")} cred, err := azidentity.NewManagedIdentityCredential(&optsClientID) handleError(err) - clientOptionsAzBlob := azblob.ClientOptions{} // Same as azcore.ClientOptions using azblob instead + clientOptionsService := service.ClientOptions{} // Same as azcore.ClientOptions using service instead - svcClient, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &clientOptionsAzBlob) + svcClient, err := service.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", accountName), cred, &clientOptionsService) handleError(err) // Set current and past time and create key @@ -306,7 +306,7 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { Expiry: to.Ptr(pastTime.UTC().Format(sas.TimeFormat)), } - udc, err := service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) + udc, err := svcClient.GetUserDelegationCredential(context.Background(), info, nil) handleError(err) fmt.Println("User Delegation Key has been created for ", accountName) @@ -336,10 +336,10 @@ func Example_service_Client_NewClientWithUserDelegationCredential() { cred, err = azidentity.NewManagedIdentityCredential(&optsResourceID) handleError(err) - svcClient, err = azblob.NewClient("svcURL", cred, &clientOptionsAzBlob) + svcClient, err = service.NewClient("svcURL", cred, &clientOptionsService) handleError(err) - udc, err = service.GetUserDelegationCredential(svcClient.URL(), context.Background(), info, nil) + udc, err = svcClient.GetUserDelegationCredential(context.Background(), info, nil) handleError(err) fmt.Println("User Delegation Key has been created for ", accountName) From 702a7d0a63e150888d01120a89c23eae38c3cbdc Mon Sep 17 00:00:00 2001 From: siminsavani-msft <77068571+siminsavani-msft@users.noreply.github.com> Date: Fri, 23 Sep 2022 11:33:41 -0700 Subject: [PATCH 22/22] Removing NewUserDelegationCredential from models.go --- sdk/storage/azblob/service/client.go | 2 +- sdk/storage/azblob/service/models.go | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/sdk/storage/azblob/service/client.go b/sdk/storage/azblob/service/client.go index b3c9288ec203..d206f0341076 100644 --- a/sdk/storage/azblob/service/client.go +++ b/sdk/storage/azblob/service/client.go @@ -97,7 +97,7 @@ func (s *Client) GetUserDelegationCredential(ctx context.Context, info KeyInfo, return nil, err } - return NewUserDelegationCredential(strings.Split(url.Host, ".")[0], udk.UserDelegationKey), nil + return exported.NewUserDelegationCredential(strings.Split(url.Host, ".")[0], udk.UserDelegationKey), nil } func (s *Client) generated() *generated.ServiceClient { diff --git a/sdk/storage/azblob/service/models.go b/sdk/storage/azblob/service/models.go index a65356b04dff..b0354cd90e3e 100644 --- a/sdk/storage/azblob/service/models.go +++ b/sdk/storage/azblob/service/models.go @@ -30,11 +30,6 @@ type UserDelegationKey = generated.UserDelegationKey // KeyInfo contains KeyInfo struct. type KeyInfo = generated.KeyInfo -// NewUserDelegationCredential creates a new UserDelegationCredential using a Storage account's name and a user delegation key from it -func NewUserDelegationCredential(accountName string, udk UserDelegationKey) *UserDelegationCredential { - return exported.NewUserDelegationCredential(accountName, udk) -} - // GetUserDelegationCredentialOptions contains optional parameters for Service.GetUserDelegationKey method type GetUserDelegationCredentialOptions struct { // placeholder for future options