Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azblob: Upgrade to STG 85-86 #21381

Merged
merged 12 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion sdk/storage/azblob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@
## 1.1.1 (Unreleased)

### Features Added
* Added support for [Cold Tier](https://learn.microsoft.com/azure/storage/blobs/access-tiers-overview?tabs=azure-portal).
* Added `CopySourceTag` option for `UploadBlobFromURLOptions`
* Added [FilterBlobs by Tags](https://learn.microsoft.com/rest/api/storageservices/find-blobs-by-tags-container) API for container client.
* Added `System` option to `ListContainersInclude` to allow listing of system containers (i.e, $web).
* Updated the SAS Version to `2021-12-02` and added `Encryption Scope` to Account SAS, Service SAS, and User Delegation SAS

### Breaking Changes

### Bugs Fixed
* Fixed issue where some requests fail with mismatch in string to sign.
* Fixed issue where some requests fail with mismatch in string to sign.
* Fixed service SAS creation where expiry time or permissions can be omitted when stored access policy is used. Fixes [#21229](https://github.com/Azure/azure-sdk-for-go/issues/21229).

* Fixed service SAS creation where expiry time or permissions can be omitted when stored access policy is used. Fixes [#21229](https://github.com/Azure/azure-sdk-for-go/issues/21229).

### Other Changes
* Updating version of azcore to 1.6.0.

## 1.1.0 (2023-07-13)

Expand Down
41 changes: 23 additions & 18 deletions sdk/storage/azblob/appendblob/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package appendblob
import (
"context"
"errors"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"io"
"os"
"time"
Expand All @@ -35,12 +36,14 @@ type Client base.CompositeClient[generated.BlobClient, generated.AppendBlobClien
func NewClient(blobURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) {
authPolicy := shared.NewStorageChallengePolicy(cred)
conOptions := shared.GetClientOptions(options)
conOptions.PerRetryPolicies = append(conOptions.PerRetryPolicies, authPolicy)
pl := runtime.NewPipeline(exported.ModuleName,
exported.ModuleVersion, runtime.PipelineOptions{},
&conOptions.ClientOptions)
plOpts := runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}

return (*Client)(base.NewAppendBlobClient(blobURL, pl, nil)), nil
azClient, err := azcore.NewClient(shared.AppendBlobClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions)
if err != nil {
return nil, err
}

return (*Client)(base.NewAppendBlobClient(blobURL, azClient, nil)), nil
}

// NewClientWithNoCredential creates an instance of Client with the specified values.
Expand All @@ -49,12 +52,13 @@ func NewClient(blobURL string, cred azcore.TokenCredential, options *ClientOptio
// - options - client options; pass nil to accept the default values
func NewClientWithNoCredential(blobURL string, options *ClientOptions) (*Client, error) {
conOptions := shared.GetClientOptions(options)
pl := runtime.NewPipeline(exported.ModuleName,
exported.ModuleVersion,
runtime.PipelineOptions{},
&conOptions.ClientOptions)

return (*Client)(base.NewAppendBlobClient(blobURL, pl, nil)), nil
azClient, err := azcore.NewClient(shared.AppendBlobClient, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions)
if err != nil {
return nil, err
}

return (*Client)(base.NewAppendBlobClient(blobURL, azClient, nil)), nil
}

// NewClientWithSharedKeyCredential creates an instance of Client with the specified values.
Expand All @@ -64,13 +68,14 @@ func NewClientWithNoCredential(blobURL string, options *ClientOptions) (*Client,
func NewClientWithSharedKeyCredential(blobURL string, cred *blob.SharedKeyCredential, options *ClientOptions) (*Client, error) {
authPolicy := exported.NewSharedKeyCredPolicy(cred)
conOptions := shared.GetClientOptions(options)
conOptions.PerRetryPolicies = append(conOptions.PerRetryPolicies, authPolicy)
pl := runtime.NewPipeline(exported.ModuleName,
exported.ModuleVersion,
runtime.PipelineOptions{},
&conOptions.ClientOptions)
plOpts := runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}

azClient, err := azcore.NewClient(shared.AppendBlobClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions)
if err != nil {
return nil, err
}

return (*Client)(base.NewAppendBlobClient(blobURL, pl, cred)), nil
return (*Client)(base.NewAppendBlobClient(blobURL, azClient, cred)), nil
}

// NewClientFromConnectionString creates an instance of Client with the specified values.
Expand Down Expand Up @@ -130,7 +135,7 @@ func (ab *Client) WithSnapshot(snapshot string) (*Client, error) {
}
p.Snapshot = snapshot

return (*Client)(base.NewAppendBlobClient(p.String(), ab.generated().Pipeline(), ab.sharedKey())), nil
return (*Client)(base.NewAppendBlobClient(p.String(), ab.generated().InternalClient(), ab.sharedKey())), nil
}

// WithVersionID creates a new AppendBlobURL object identical to the source but with the specified version id.
Expand All @@ -142,7 +147,7 @@ func (ab *Client) WithVersionID(versionID string) (*Client, error) {
}
p.VersionID = versionID

return (*Client)(base.NewAppendBlobClient(p.String(), ab.generated().Pipeline(), ab.sharedKey())), nil
return (*Client)(base.NewAppendBlobClient(p.String(), ab.generated().InternalClient(), ab.sharedKey())), nil
}

// Create creates a 0-size append blob. Call AppendBlock to append data to an append blob.
Expand Down
144 changes: 144 additions & 0 deletions sdk/storage/azblob/appendblob/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"crypto/md5"
"encoding/binary"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
"hash/crc64"
"io"
"math/rand"
Expand Down Expand Up @@ -371,6 +372,149 @@ func (s *AppendBlobUnrecordedTestsSuite) TestAppendBlockFromURL() {
_require.Equal(destBuffer, sourceData)
}

func (s *AppendBlobUnrecordedTestsSuite) TestBlobEncryptionScopeSAS() {
_require := require.New(s.T())
testName := s.T().Name()
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

containerName := testcommon.GenerateContainerName(testName)
containerClient := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)

blobClient := containerClient.NewAppendBlobClient(testcommon.GenerateBlobName("appendsrc"))

// Get source abClient URL with SAS for AppendBlockFromURL.
blobParts, _ := blob.ParseURL(blobClient.URL())

encryptionScope, err := testcommon.GetRequiredEnv(testcommon.EncryptionScopeEnvVar)
_require.Nil(err)
credential, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault)
_require.Nil(err)
perms := sas.BlobPermissions{Read: true, Create: true, Write: true, Delete: true}

blobParts.SAS, err = sas.BlobSignatureValues{
Protocol: sas.ProtocolHTTPS, // Users MUST use HTTPS (not HTTP)
ExpiryTime: time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
ContainerName: blobParts.ContainerName,
BlobName: blobParts.BlobName,
Permissions: perms.String(),
EncryptionScope: encryptionScope,
}.SignWithSharedKey(credential)
_require.NoError(err)

blobURLWithSAS := blobParts.String()

// create new client with sas url
blobClient, err = appendblob.NewClientWithNoCredential(blobURLWithSAS, nil)
_require.Nil(err)

createResponse, err := blobClient.Create(context.Background(), nil)
_require.NoError(err)
_require.Equal(*createResponse.EncryptionScope, encryptionScope)
}

func (s *AppendBlobUnrecordedTestsSuite) TestAccountEncryptionScopeSAS() {
_require := require.New(s.T())
testName := s.T().Name()
svcClient, err := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil)
_require.NoError(err)

containerName := testcommon.GenerateContainerName(testName)
containerClient := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, containerClient)

blobName := testcommon.GenerateBlobName("appendsrc")
blobClient := containerClient.NewAppendBlobClient(blobName)

// Get blob URL with SAS for AppendBlockFromURL.
blobParts, _ := blob.ParseURL(blobClient.URL())

encryptionScope, err := testcommon.GetRequiredEnv(testcommon.EncryptionScopeEnvVar)
_require.Nil(err)

credential, err := testcommon.GetGenericSharedKeyCredential(testcommon.TestAccountDefault)
_require.Nil(err)

blobParts.SAS, err = sas.AccountSignatureValues{
Protocol: sas.ProtocolHTTPS, // Users MUST use HTTPS (not HTTP)
ExpiryTime: time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
Permissions: to.Ptr(sas.AccountPermissions{Read: true, Create: true, Write: true, Delete: true}).String(),
ResourceTypes: to.Ptr(sas.AccountResourceTypes{Service: true, Container: true, Object: true}).String(),
EncryptionScope: encryptionScope,
}.SignWithSharedKey(credential)
_require.NoError(err)

blobURLWithSAS := blobParts.String()
blobClient, err = appendblob.NewClientWithNoCredential(blobURLWithSAS, nil)
_require.NoError(err)

createResp, err := blobClient.Create(context.Background(), nil)
_require.NoError(err)
_require.NotNil(createResp)
_require.Equal(*createResp.EncryptionScope, encryptionScope)
}

func (s *AppendBlobUnrecordedTestsSuite) TestGetUserDelegationEncryptionScopeSAS() {
_require := require.New(s.T())
testName := s.T().Name()
accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault)
_require.Greater(len(accountName), 0)

cred, err := testcommon.GetGenericTokenCredential()
_require.NoError(err)

svcClient, err := service.NewClient("https://"+accountName+".blob.core.windows.net/", cred, nil)
_require.NoError(err)

containerName := testcommon.GenerateContainerName(testName)
cntClientTokenCred := testcommon.CreateNewContainer(context.Background(), _require, containerName, svcClient)
defer testcommon.DeleteContainer(context.Background(), _require, cntClientTokenCred)

blobName := testcommon.GenerateBlobName("appendsrc")
blobClient := cntClientTokenCred.NewAppendBlobClient(blobName)

// Set current and past time and create key
now := time.Now().UTC().Add(-10 * time.Second)
expiry := now.Add(2 * time.Hour)
info := service.KeyInfo{
Start: to.Ptr(now.UTC().Format(sas.TimeFormat)),
Expiry: to.Ptr(expiry.UTC().Format(sas.TimeFormat)),
}

udc, err := svcClient.GetUserDelegationCredential(context.Background(), info, nil)
_require.NoError(err)

// get permissions and details for sas
encryptionScope, err := testcommon.GetRequiredEnv(testcommon.EncryptionScopeEnvVar)
_require.Nil(err)

permissions := sas.BlobPermissions{Read: true, Create: true, Write: true, List: true, Add: true, Delete: true}

blobParts, _ := blob.ParseURL(blobClient.URL())

// Create Blob Signature Values with desired permissions and sign with user delegation credential
blobParts.SAS, err = sas.BlobSignatureValues{
Protocol: sas.ProtocolHTTPS,
StartTime: time.Now().UTC().Add(time.Second * -10),
ExpiryTime: time.Now().UTC().Add(15 * time.Minute),
Permissions: permissions.String(),
ContainerName: containerName,
EncryptionScope: encryptionScope,
}.SignWithUserDelegation(udc)
_require.NoError(err)

blobURLWithSAS := blobParts.String()
blobClient, err = appendblob.NewClientWithNoCredential(blobURLWithSAS, nil)
_require.NoError(err)

createResp, err := blobClient.Create(context.Background(), nil)
_require.NoError(err)
_require.NotNil(createResp)
_require.Equal(*createResp.EncryptionScope, encryptionScope)

}

func (s *AppendBlobUnrecordedTestsSuite) TestAppendBlockFromURLWithMD5() {
_require := require.New(s.T())
testName := s.T().Name()
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/azblob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "go",
"TagPrefix": "go/storage/azblob",
"Tag": "go/storage/azblob_0776f1b95b"
"Tag": "go/storage/azblob_7a25fb98e8"
}
34 changes: 22 additions & 12 deletions sdk/storage/azblob/blob/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package blob

import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
"io"
"os"
Expand Down Expand Up @@ -37,10 +38,13 @@ type Client base.Client[generated.BlobClient]
func NewClient(blobURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) {
authPolicy := shared.NewStorageChallengePolicy(cred)
conOptions := shared.GetClientOptions(options)
conOptions.PerRetryPolicies = append(conOptions.PerRetryPolicies, authPolicy)
pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions)
plOpts := runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}

return (*Client)(base.NewBlobClient(blobURL, pl, &cred)), nil
azClient, err := azcore.NewClient(shared.BlobClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions)
if err != nil {
return nil, err
}
return (*Client)(base.NewBlobClient(blobURL, azClient, &cred)), nil
}

// NewClientWithNoCredential creates an instance of Client with the specified values.
Expand All @@ -49,9 +53,12 @@ func NewClient(blobURL string, cred azcore.TokenCredential, options *ClientOptio
// - options - client options; pass nil to accept the default values
func NewClientWithNoCredential(blobURL string, options *ClientOptions) (*Client, error) {
conOptions := shared.GetClientOptions(options)
pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions)

return (*Client)(base.NewBlobClient(blobURL, pl, nil)), nil
azClient, err := azcore.NewClient(shared.BlobClient, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions)
if err != nil {
return nil, err
}
return (*Client)(base.NewBlobClient(blobURL, azClient, nil)), nil
}

// NewClientWithSharedKeyCredential creates an instance of Client with the specified values.
Expand All @@ -61,10 +68,13 @@ func NewClientWithNoCredential(blobURL string, options *ClientOptions) (*Client,
func NewClientWithSharedKeyCredential(blobURL string, cred *SharedKeyCredential, options *ClientOptions) (*Client, error) {
authPolicy := exported.NewSharedKeyCredPolicy(cred)
conOptions := shared.GetClientOptions(options)
conOptions.PerRetryPolicies = append(conOptions.PerRetryPolicies, authPolicy)
pl := runtime.NewPipeline(exported.ModuleName, exported.ModuleVersion, runtime.PipelineOptions{}, &conOptions.ClientOptions)
plOpts := runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}

return (*Client)(base.NewBlobClient(blobURL, pl, cred)), nil
azClient, err := azcore.NewClient(shared.BlobClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions)
if err != nil {
return nil, err
}
return (*Client)(base.NewBlobClient(blobURL, azClient, cred)), nil
}

// NewClientFromConnectionString creates an instance of Client with the specified values.
Expand Down Expand Up @@ -116,7 +126,7 @@ func (b *Client) WithSnapshot(snapshot string) (*Client, error) {
}
p.Snapshot = snapshot

return (*Client)(base.NewBlobClient(p.String(), b.generated().Pipeline(), b.credential())), nil
return (*Client)(base.NewBlobClient(p.String(), b.generated().InternalClient(), b.credential())), nil
}

// WithVersionID creates a new AppendBlobURL object identical to the source but with the specified version id.
Expand All @@ -128,7 +138,7 @@ func (b *Client) WithVersionID(versionID string) (*Client, error) {
}
p.VersionID = versionID

return (*Client)(base.NewBlobClient(p.String(), b.generated().Pipeline(), b.credential())), nil
return (*Client)(base.NewBlobClient(p.String(), b.generated().InternalClient(), b.credential())), nil
}

// Delete marks the specified blob or snapshot for deletion. The blob is later deleted during garbage collection.
Expand Down Expand Up @@ -261,8 +271,8 @@ func (b *Client) SetLegalHold(ctx context.Context, legalHold bool, options *SetL
// CopyFromURL synchronously copies the data at the source URL to a block blob, with sizes up to 256 MB.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/copy-blob-from-url.
func (b *Client) CopyFromURL(ctx context.Context, copySource string, options *CopyFromURLOptions) (CopyFromURLResponse, error) {
copyOptions, smac, mac, lac := options.format()
resp, err := b.generated().CopyFromURL(ctx, copySource, copyOptions, smac, mac, lac)
copyOptions, smac, mac, lac, cpkScopeInfo := options.format()
resp, err := b.generated().CopyFromURL(ctx, copySource, copyOptions, smac, mac, lac, cpkScopeInfo)
return resp, err
}

Expand Down
Loading