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

Encryption Scope SAS #21294

Merged
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
3 changes: 2 additions & 1 deletion sdk/storage/azblob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* 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](https://learn.microsoft.com/rest/api/storageservices/find-blobs-by-tags-container) by tags API for container client.
* Added `System` option to `ListContainersInclude` to allow listing of system containers.
* Added `System` option to `ListContainersInclude` to allow listing of system containers.
* Updated the SAS Version to `2021-12-02` and added `Encryption Scope` to Account SAS, Service SAS, and User Delegation SAS

### Breaking Changes

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
29 changes: 16 additions & 13 deletions sdk/storage/azblob/sas/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ type UserDelegationCredential = exported.UserDelegationCredential
// AccountSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage account.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/constructing-an-account-sas
type AccountSignatureValues struct {
Version string `param:"sv"` // If not specified, this format to SASVersion
Protocol Protocol `param:"spr"` // See the SASProtocol* constants
StartTime time.Time `param:"st"` // Not specified if IsZero
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
Permissions string `param:"sp"` // Create by initializing AccountPermissions and then call String()
IPRange IPRange `param:"sip"`
ResourceTypes string `param:"srt"` // Create by initializing AccountResourceTypes and then call String()
Version string `param:"sv"` // If not specified, this format to SASVersion
Protocol Protocol `param:"spr"` // See the SASProtocol* constants
StartTime time.Time `param:"st"` // Not specified if IsZero
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
Permissions string `param:"sp"` // Create by initializing AccountPermissions and then call String()
IPRange IPRange `param:"sip"`
ResourceTypes string `param:"srt"` // Create by initializing AccountResourceTypes and then call String()
EncryptionScope string `param:"ses"`
}

// SignWithSharedKey uses an account's shared key credential to sign this signature values to produce
Expand Down Expand Up @@ -68,6 +69,7 @@ func (v AccountSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKey
v.IPRange.String(),
string(v.Protocol),
v.Version,
v.EncryptionScope,
""}, // That is right, the account SAS requires a terminating extra newline
"\n")

Expand All @@ -77,12 +79,13 @@ func (v AccountSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKey
}
p := QueryParameters{
// Common SAS parameters
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
encryptionScope: v.EncryptionScope,

// Account-specific SAS parameters
services: "b", // will always be "b"
Expand Down
13 changes: 12 additions & 1 deletion sdk/storage/azblob/sas/query_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (

var (
// Version is the default version encoded in the SAS token.
Version = "2020-02-10"
Version = "2021-12-02"
)

// TimeFormats ISO 8601 format.
Expand Down Expand Up @@ -143,6 +143,7 @@ type QueryParameters struct {
authorizedObjectID string `param:"saoid"`
unauthorizedObjectID string `param:"suoid"`
correlationID string `param:"scid"`
encryptionScope string `param:"ses"`
// private member used for startTime and expiryTime formatting.
stTimeFormat string
seTimeFormat string
Expand All @@ -163,6 +164,11 @@ func (p *QueryParameters) SignedCorrelationID() string {
return p.correlationID
}

// EncryptionScope returns encryptionScope
func (p *QueryParameters) EncryptionScope() string {
return p.encryptionScope
}

// SignedOID returns signedOID.
func (p *QueryParameters) SignedOID() string {
return p.signedOID
Expand Down Expand Up @@ -355,6 +361,9 @@ func (p *QueryParameters) Encode() string {
if p.correlationID != "" {
v.Add("scid", p.correlationID)
}
if p.encryptionScope != "" {
v.Add("ses", p.encryptionScope)
}

return v.Encode()
}
Expand Down Expand Up @@ -429,6 +438,8 @@ func NewQueryParameters(values url.Values, deleteSASParametersFromValues bool) Q
p.unauthorizedObjectID = val
case "scid":
p.correlationID = val
case "ses":
p.encryptionScope = val
default:
isSASKey = false // We didn't recognize the query parameter
}
Expand Down
33 changes: 19 additions & 14 deletions sdk/storage/azblob/sas/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type BlobSignatureValues struct {
AuthorizedObjectID string // saoid
UnauthorizedObjectID string // suoid
CorrelationID string // scid
EncryptionScope string `param:"ses"`
}

func getDirectoryDepth(path string) string {
Expand Down Expand Up @@ -103,7 +104,8 @@ func (v BlobSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKeyCre
string(v.Protocol),
v.Version,
resource,
snapshotTime, // signed timestamp
snapshotTime, // signed timestamp
v.EncryptionScope,
v.CacheControl, // rscc
v.ContentDisposition, // rscd
v.ContentEncoding, // rsce
souravgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -118,12 +120,13 @@ func (v BlobSignatureValues) SignWithSharedKey(sharedKeyCredential *SharedKeyCre

p := QueryParameters{
// Common SAS parameters
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
encryptionScope: v.EncryptionScope,

// Container/Blob-specific SAS parameters
resource: resource,
Expand Down Expand Up @@ -211,7 +214,8 @@ func (v BlobSignatureValues) SignWithUserDelegation(userDelegationCredential *Us
string(v.Protocol),
v.Version,
resource,
snapshotTime, // signed timestamp
snapshotTime, // signed timestamp
v.EncryptionScope,
v.CacheControl, // rscc
v.ContentDisposition, // rscd
v.ContentEncoding, // rsce
Expand All @@ -226,12 +230,13 @@ func (v BlobSignatureValues) SignWithUserDelegation(userDelegationCredential *Us

p := QueryParameters{
// Common SAS parameters
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
version: v.Version,
protocol: v.Protocol,
startTime: v.StartTime,
expiryTime: v.ExpiryTime,
permissions: v.Permissions,
ipRange: v.IPRange,
encryptionScope: v.EncryptionScope,

// Container/Blob-specific SAS parameters
resource: resource,
Expand Down