Skip to content

Commit

Permalink
Encryption Scope SAS (#21294)
Browse files Browse the repository at this point in the history
* Adding encryption sas to blob, account, and identity sas

* Fixing issues with Blob SAS

* Undo some changes

* Undo some changes pt 2

* Undo some changes pt 3

* Adding doc comment

* Updating variable names in the tests and updated account sas test

* Updating tests

* Adding back comment

* Updating CHANGELOG.md

* Update sdk/storage/azblob/sas/query_params.go

Co-authored-by: Sourav Gupta <[email protected]>

* Update sdk/storage/azblob/CHANGELOG.md

Co-authored-by: Sourav Gupta <[email protected]>

* Removing SI from Account SAS

---------

Co-authored-by: Sourav Gupta <[email protected]>
  • Loading branch information
siminsavani-msft and souravgupta-msft committed Aug 16, 2023
1 parent 57f7bff commit c669617
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 29 deletions.
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
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

0 comments on commit c669617

Please sign in to comment.