Skip to content

Commit

Permalink
Move SAS-related functionality to the sas package (Azure#19010)
Browse files Browse the repository at this point in the history
* Move SAS-related functionality to the sas package

SAS functionality has been moved out of internal/exported to the sas
package.  All type aliases to the former types have been removed.
Removed "SAS" prefix from funcs and types in the sas package.
URL parsing has been moved out of internal/exported to the blob package.

* fix doc comment

* reduced and cleaned up more public surface area

* fix doc comment

* comment out dead code for linter
  • Loading branch information
jhendrixMSFT authored Sep 7, 2022
1 parent 4222101 commit 46e2040
Show file tree
Hide file tree
Showing 29 changed files with 367 additions and 437 deletions.
4 changes: 2 additions & 2 deletions sdk/storage/azblob/appendblob/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (ab *Client) URL() string {
// WithSnapshot creates a new AppendBlobURL object identical to the source but with the specified snapshot timestamp.
// Pass "" to remove the snapshot returning a URL to the base blob.
func (ab *Client) WithSnapshot(snapshot string) (*Client, error) {
p, err := exported.ParseURL(ab.URL())
p, err := blob.ParseURL(ab.URL())
if err != nil {
return nil, err
}
Expand All @@ -110,7 +110,7 @@ func (ab *Client) WithSnapshot(snapshot string) (*Client, error) {
// WithVersionID creates a new AppendBlobURL object identical to the source but with the specified version id.
// Pass "" to remove the versionID returning a URL to the base blob.
func (ab *Client) WithVersionID(versionID string) (*Client, error) {
p, err := exported.ParseURL(ab.URL())
p, err := blob.ParseURL(ab.URL())
if err != nil {
return nil, err
}
Expand Down
28 changes: 16 additions & 12 deletions sdk/storage/azblob/blob/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
)

// ClientOptions contains the optional parameters when creating a Client.
Expand Down Expand Up @@ -95,7 +96,7 @@ func (b *Client) URL() string {
// WithSnapshot creates a new Client object identical to the source but with the specified snapshot timestamp.
// Pass "" to remove the snapshot returning a URL to the base blob.
func (b *Client) WithSnapshot(snapshot string) (*Client, error) {
p, err := exported.ParseURL(b.URL())
p, err := ParseURL(b.URL())
if err != nil {
return nil, err
}
Expand All @@ -107,7 +108,7 @@ func (b *Client) WithSnapshot(snapshot string) (*Client, error) {
// WithVersionID creates a new AppendBlobURL object identical to the source but with the specified version id.
// Pass "" to remove the versionID returning a URL to the base blob.
func (b *Client) WithVersionID(versionID string) (*Client, error) {
p, err := exported.ParseURL(b.URL())
p, err := ParseURL(b.URL())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -226,26 +227,29 @@ func (b *Client) CopyFromURL(ctx context.Context, copySource string, options *Co
return resp, err
}

// GetSASToken is a convenience method for generating a SAS token for the currently pointed at blob.
// GetSASURL is a convenience method for generating a SAS token for the currently pointed at blob.
// It can only be used if the credential supplied during creation was a SharedKeyCredential.
func (b *Client) GetSASToken(permissions SASPermissions, start time.Time, expiry time.Time) (string, error) {
urlParts, _ := exported.ParseURL(b.URL())

t, err := time.Parse(exported.SnapshotTimeFormat, urlParts.Snapshot)
func (b *Client) GetSASURL(permissions sas.BlobPermissions, start time.Time, expiry time.Time) (string, error) {
if b.sharedKey() == nil {
return "", errors.New("credential is not a SharedKeyCredential. SAS can only be signed with a SharedKeyCredential")
}

urlParts, err := ParseURL(b.URL())
if err != nil {
t = time.Time{}
return "", err
}

if b.sharedKey() == nil {
return "", errors.New("credential is not a SharedKeyCredential. SAS can only be signed with a SharedKeyCredential")
t, err := time.Parse(SnapshotTimeFormat, urlParts.Snapshot)

if err != nil {
t = time.Time{}
}

qps, err := exported.BlobSASSignatureValues{
qps, err := sas.BlobSignatureValues{
ContainerName: urlParts.ContainerName,
BlobName: urlParts.BlobName,
SnapshotTime: t,
Version: exported.SASVersion,
Version: sas.Version,

Permissions: permissions.String(),

Expand Down
35 changes: 17 additions & 18 deletions sdk/storage/azblob/blob/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/testcommon"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -78,7 +77,7 @@ func (s *BlobUnrecordedTestsSuite) TestCreateBlobClient() {
blobName := testcommon.GenerateBlobName(testName)
bbClient := testcommon.GetBlockBlobClient(blobName, containerClient)

blobURLParts, err := azblob.ParseURL(bbClient.URL())
blobURLParts, err := blob.ParseURL(bbClient.URL())
_require.Nil(err)
_require.Equal(blobURLParts.BlobName, blobName)
_require.Equal(blobURLParts.ContainerName, containerName)
Expand Down Expand Up @@ -108,19 +107,19 @@ func (s *BlobUnrecordedTestsSuite) TestCreateBlobClientWithSnapshotAndSAS() {
credential, err := testcommon.GetGenericCredential(testcommon.TestAccountDefault)
_require.Nil(err)

sasQueryParams, err := service.SASSignatureValues{
Protocol: service.SASProtocolHTTPS,
sasQueryParams, err := sas.AccountSignatureValues{
Protocol: sas.ProtocolHTTPS,
ExpiryTime: currentTime,
Permissions: to.Ptr(service.SASPermissions{Read: true, List: true}).String(),
Services: to.Ptr(service.SASServices{Blob: true}).String(),
ResourceTypes: to.Ptr(service.SASResourceTypes{Container: true, Object: true}).String(),
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)
_require.Nil(err)

parts, err := exported.ParseURL(bbClient.URL())
parts, err := blob.ParseURL(bbClient.URL())
_require.Nil(err)
parts.SAS = sasQueryParams
parts.Snapshot = currentTime.Format(service.SnapshotTimeFormat)
parts.Snapshot = currentTime.Format(blob.SnapshotTimeFormat)
blobURLParts := parts.String()

// The snapshot format string is taken from the snapshotTimeFormat value in parsing_urls.go. The field is not public, so
Expand Down Expand Up @@ -150,19 +149,19 @@ func (s *BlobUnrecordedTestsSuite) TestCreateBlobClientWithSnapshotAndSASUsingCo

credential, err := testcommon.GetGenericCredential(testcommon.TestAccountDefault)
_require.Nil(err)
sasQueryParams, err := service.SASSignatureValues{
Protocol: service.SASProtocolHTTPS,
sasQueryParams, err := sas.AccountSignatureValues{
Protocol: sas.ProtocolHTTPS,
ExpiryTime: currentTime,
Permissions: to.Ptr(service.SASPermissions{Read: true, List: true}).String(),
Services: to.Ptr(service.SASServices{Blob: true}).String(),
ResourceTypes: to.Ptr(service.SASResourceTypes{Container: true, Object: true}).String(),
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)
_require.Nil(err)

parts, err := exported.ParseURL(bbClient.URL())
parts, err := blob.ParseURL(bbClient.URL())
_require.Nil(err)
parts.SAS = sasQueryParams
parts.Snapshot = currentTime.Format(service.SnapshotTimeFormat)
parts.Snapshot = currentTime.Format(blob.SnapshotTimeFormat)
blobURLParts := parts.String()

// The snapshot format string is taken from the snapshotTimeFormat value in parsing_urls.go. The field is not public, so
Expand Down Expand Up @@ -3146,7 +3145,7 @@ func (s *BlobRecordedTestsSuite) TestBlobClientPartsSASQueryTimes() {
"st=" + url.QueryEscape(StartTimesInputs[i]) + "&" +
"sv=2019-10-10"

parts, _ := azblob.ParseURL(urlString)
parts, _ := blob.ParseURL(urlString)
_require.Equal(parts.Scheme, "https")
_require.Equal(parts.Host, "myaccount.blob.core.windows.net")
_require.Equal(parts.ContainerName, "mycontainer")
Expand Down
7 changes: 5 additions & 2 deletions sdk/storage/azblob/blob/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

package blob

import "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated"
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"
)

const (
CountToEnd = 0

SnapshotTimeFormat = "2006-01-02T15:04:05.0000000Z07:00"
SnapshotTimeFormat = exported.SnapshotTimeFormat

// DefaultDownloadBlockSize is default block size
DefaultDownloadBlockSize = int64(4 * 1024 * 1024) // 4MB
Expand Down
16 changes: 16 additions & 0 deletions sdk/storage/azblob/blob/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,19 @@ func Example_blob_Client_StartCopyFromURL() {
}
fmt.Printf("Copy from %s to %s: ID=%s, Status=%s\n", src, blobClient.URL(), copyID, copyStatus)
}

// This example demonstrates splitting a URL into its parts so you can examine and modify the URL in an Azure Storage fluent way.
func ExampleParseURL() {
// Here is an example of a blob snapshot.
u := "https://myaccount.blob.core.windows.net/mycontainter/ReadMe.txt?" +
"snapshot=2011-03-09T01:42:34Z&" +
"sv=2015-02-21&sr=b&st=2111-01-09T01:42:34.936Z&se=2222-03-09T01:42:34.936Z&sp=rw&sip=168.1.5.60-168.1.5.70&" +
"spr=https,http&si=myIdentifier&ss=bf&srt=s&sig=92836758923659283652983562=="

// Breaking the URL down into it's parts by conversion to URLParts
parts, _ := blob.ParseURL(u)

// The URLParts allows access to individual portions of a Blob URL
fmt.Printf("Host: %s\nContainerName: %s\nBlobName: %s\nSnapshot: %s\n", parts.Host, parts.ContainerName, parts.BlobName, parts.Snapshot)
fmt.Printf("Version: %s\nResource: %s\nStartTime: %s\nExpiryTime: %s\nPermissions: %s\n", parts.SAS.Version(), parts.SAS.Resource(), parts.SAS.StartTime(), parts.SAS.ExpiryTime(), parts.SAS.Permissions())
}
16 changes: 0 additions & 16 deletions sdk/storage/azblob/blob/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,9 @@ type CpkScopeInfo = generated.CpkScopeInfo
// HTTPHeaders contains a group of parameters for the BlobClient.SetHTTPHeaders method.
type HTTPHeaders = generated.BlobHTTPHeaders

// SASProtocol indicates the http/https.
type SASProtocol = exported.SASProtocol

// IPRange represents a SAS IP range's start IP and (optionally) end IP.
type IPRange = exported.IPRange

// SASQueryParameters object represents the components that make up an Azure Storage SAS' query parameters.
// You parse a map of query parameters into its fields by calling Sign(). You add the components
// to a query parameter map by calling AddToValues().
// NOTE: Changing any field requires computing a new SAS signature using a XxxSASSignatureValues type.
type SASQueryParameters = exported.SASQueryParameters

// SourceModifiedAccessConditions contains a group of parameters for the BlobClient.StartCopyFromURL method.
type SourceModifiedAccessConditions = generated.SourceModifiedAccessConditions

// SASPermissions type simplifies creating the permissions string for an Azure Storage blob SAS.
// Initialize an instance of this type and then call its String method to set BlobSASSignatureValues's Permissions field.
type SASPermissions = exported.BlobSASPermissions

// Tags represent map of blob index tags
type Tags = generated.BlobTag

Expand Down
14 changes: 9 additions & 5 deletions sdk/storage/azblob/blob/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ package blob

import (
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
)

// ObjectReplicationRules struct
Expand Down Expand Up @@ -69,7 +68,12 @@ func ParseHTTPHeaders(resp GetPropertiesResponse) HTTPHeaders {
}
}

// ParseSASTimeString try to parse sas time string.
func ParseSASTimeString(val string) (t time.Time, timeFormat string, err error) {
return exported.ParseSASTimeString(val)
// 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

// ParseURL parses a URL initializing URLParts' fields including any SAS-related & snapshot query parameters. Any other
// query parameters remain in the UnparsedParams field. This method overwrites all fields in the URLParts object.
func ParseURL(u string) (URLParts, error) {
return sas.ParseURL(u)
}
4 changes: 2 additions & 2 deletions sdk/storage/azblob/blockblob/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (bb *Client) BlobClient() *blob.Client {
// WithSnapshot creates a new Client object identical to the source but with the specified snapshot timestamp.
// Pass "" to remove the snapshot returning a URL to the base blob.
func (bb *Client) WithSnapshot(snapshot string) (*Client, error) {
p, err := exported.ParseURL(bb.URL())
p, err := blob.ParseURL(bb.URL())
if err != nil {
return nil, err
}
Expand All @@ -117,7 +117,7 @@ func (bb *Client) WithSnapshot(snapshot string) (*Client, error) {
// WithVersionID creates a new AppendBlobURL object identical to the source but with the specified version id.
// Pass "" to remove the versionID returning a URL to the base blob.
func (bb *Client) WithVersionID(versionID string) (*Client, error) {
p, err := exported.ParseURL(bb.URL())
p, err := blob.ParseURL(bb.URL())
if err != nil {
return nil, err
}
Expand Down
14 changes: 7 additions & 7 deletions sdk/storage/azblob/blockblob/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/testcommon"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -1159,16 +1159,16 @@ func (s *BlockBlobUnrecordedTestsSuite) TestSetTierOnCopyBlockBlobFromURL() {
if err != nil {
s.T().Fatal("Couldn't fetch credential because " + err.Error())
}
sasQueryParams, err := service.SASSignatureValues{
Protocol: service.SASProtocolHTTPS,
sasQueryParams, err := sas.AccountSignatureValues{
Protocol: sas.ProtocolHTTPS,
ExpiryTime: expiryTime,
Permissions: to.Ptr(service.SASPermissions{Read: true, List: true}).String(),
Services: to.Ptr(service.SASServices{Blob: true}).String(),
ResourceTypes: to.Ptr(service.SASResourceTypes{Container: true, Object: true}).String(),
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)
_require.Nil(err)

srcBlobParts, _ := azblob.ParseURL(srcBlob.URL())
srcBlobParts, _ := blob.ParseURL(srcBlob.URL())
srcBlobParts.SAS = sasQueryParams
srcBlobURLWithSAS := srcBlobParts.String()

Expand Down
25 changes: 4 additions & 21 deletions sdk/storage/azblob/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/sas"
)

// SharedKeyCredential contains an account's name and its primary or secondary key.
Expand All @@ -19,30 +20,12 @@ func NewSharedKeyCredential(accountName, accountKey string) (*SharedKeyCredentia
return exported.NewSharedKeyCredential(accountName, accountKey)
}

// 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

// URLParts object represents the components that make up an Azure Storage Container/Blob URL. You parse an
// existing URL into its parts by calling NewBlobURLParts(). You construct a URL from parts by calling URL().
// 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 = exported.URLParts
type URLParts = sas.URLParts

// ParseURL parses a URL initializing URLParts' fields including any SAS-related & snapshot query parameters. Any other
// query parameters remain in the UnparsedParams field. This method overwrites all fields in the URLParts object.
func ParseURL(u string) (URLParts, error) {
return exported.ParseURL(u)
return sas.ParseURL(u)
}

// SASQueryParameters object represents the components that make up an Azure Storage SAS' query parameters.
// You parse a map of query parameters into its fields by calling NewSASQueryParameters(). You add the components
// to a query parameter map by calling AddToValues().
// NOTE: Changing any field requires computing a new SAS signature using a XxxSASSignatureValues type.
// https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
type SASQueryParameters = exported.SASQueryParameters

// SASProtocol indicates the protocol in use for the SAS URL (http/https).
type SASProtocol = exported.SASProtocol

// IPRange represents a SAS IP range's start IP and (optionally) end IP.
type IPRange = exported.IPRange
11 changes: 6 additions & 5 deletions sdk/storage/azblob/container/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/generated"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas"
)

// ClientOptions contains the optional parameters when creating a Client.
Expand Down Expand Up @@ -276,20 +277,20 @@ func (c *Client) NewListBlobsHierarchyPager(delimiter string, o *ListBlobsHierar

// GetSASURL 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 SharedKeyCredential.
func (c *Client) GetSASURL(permissions SASPermissions, start time.Time, expiry time.Time) (string, error) {
func (c *Client) GetSASURL(permissions sas.ContainerPermissions, start time.Time, expiry time.Time) (string, error) {
if c.sharedKey() == nil {
return "", errors.New("SAS can only be signed with a SharedKeyCredential")
}

urlParts, err := exported.ParseURL(c.URL())
urlParts, err := blob.ParseURL(c.URL())
if err != nil {
return "", err
}

// Containers do not have snapshots, nor versions.
qps, err := SASSignatureValues{
Version: exported.SASVersion,
Protocol: exported.SASProtocolHTTPS,
qps, err := sas.BlobSignatureValues{
Version: sas.Version,
Protocol: sas.ProtocolHTTPS,
ContainerName: urlParts.ContainerName,
Permissions: permissions.String(),
StartTime: start.UTC(),
Expand Down
Loading

0 comments on commit 46e2040

Please sign in to comment.