Skip to content

Commit

Permalink
Implement copy source authorization (#317)
Browse files Browse the repository at this point in the history
* Implement copy source authorization

* Implement testing

* Nil check

* Bump timeout

* Fix time format

* Reduce test code dupe

* Correct timezone issue
  • Loading branch information
adreed-msft committed Apr 25, 2022
1 parent 7e62913 commit e7f2275
Show file tree
Hide file tree
Showing 21 changed files with 427 additions and 1,157 deletions.
6 changes: 3 additions & 3 deletions azblob/access_conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ type ModifiedAccessConditions struct {
}

// pointers is for internal infrastructure. It returns the fields as pointers.
func (ac ModifiedAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *string, inme *string) {
func (ac ModifiedAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *ETag, inme *ETag) {
if !ac.IfModifiedSince.IsZero() {
ims = &ac.IfModifiedSince
}
if !ac.IfUnmodifiedSince.IsZero() {
ius = &ac.IfUnmodifiedSince
}
if ac.IfMatch != ETagNone {
ime = (*string)(&ac.IfMatch)
ime = &ac.IfMatch
}
if ac.IfNoneMatch != ETagNone {
inme = (*string)(&ac.IfNoneMatch)
inme = &ac.IfNoneMatch
}
return
}
Expand Down
4 changes: 2 additions & 2 deletions azblob/url_append_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (ab AppendBlobURL) AppendBlock(ctx context.Context, body io.ReadSeeker, ac

// AppendBlockFromURL copies a new block of data from source URL to the end of the existing append blob.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/append-block-from-url.
func (ab AppendBlobURL) AppendBlockFromURL(ctx context.Context, sourceURL url.URL, offset int64, count int64, destinationAccessConditions AppendBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, transactionalMD5 []byte, cpk ClientProvidedKeyOptions) (*AppendBlobAppendBlockFromURLResponse, error) {
func (ab AppendBlobURL) AppendBlockFromURL(ctx context.Context, sourceURL url.URL, offset int64, count int64, destinationAccessConditions AppendBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, transactionalMD5 []byte, cpk ClientProvidedKeyOptions, sourceAuthorization TokenCredential) (*AppendBlobAppendBlockFromURLResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers()
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
ifAppendPositionEqual, ifMaxSizeLessThanOrEqual := destinationAccessConditions.AppendPositionAccessConditions.pointers()
Expand All @@ -111,7 +111,7 @@ func (ab AppendBlobURL) AppendBlockFromURL(ctx context.Context, sourceURL url.UR
ifMaxSizeLessThanOrEqual, ifAppendPositionEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
nil, // Blob ifTags
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, nil)
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, tokenCredentialPointers(sourceAuthorization))
}

type AppendBlobAccessConditions struct {
Expand Down
12 changes: 6 additions & 6 deletions azblob/url_block_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ func (bb BlockBlobURL) StageBlock(ctx context.Context, base64BlockID string, bod
// StageBlockFromURL copies the specified block from a source URL to the block blob's "staging area" to be later committed by a call to CommitBlockList.
// If count is CountToEnd (0), then data is read from specified offset to the end.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/put-block-from-url.
func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID string, sourceURL url.URL, offset int64, count int64, destinationAccessConditions LeaseAccessConditions, sourceAccessConditions ModifiedAccessConditions, cpk ClientProvidedKeyOptions) (*BlockBlobStageBlockFromURLResponse, error) {
func (bb BlockBlobURL) StageBlockFromURL(ctx context.Context, base64BlockID string, sourceURL url.URL, offset int64, count int64, destinationAccessConditions LeaseAccessConditions, sourceAccessConditions ModifiedAccessConditions, cpk ClientProvidedKeyOptions, sourceAuthorization TokenCredential) (*BlockBlobStageBlockFromURLResponse, error) {
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
return bb.bbClient.StageBlockFromURL(ctx, base64BlockID, 0, sourceURL.String(), httpRange{offset: offset, count: count}.pointers(), nil, nil, nil,
cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK
cpk.EncryptionScope, // CPK-N
destinationAccessConditions.pointers(), sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, nil)
destinationAccessConditions.pointers(), sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, tokenCredentialPointers(sourceAuthorization))
}

// CommitBlockList writes a blob by specifying the list of block IDs that make up the blob.
Expand Down Expand Up @@ -146,7 +146,7 @@ func (bb BlockBlobURL) GetBlockList(ctx context.Context, listType BlockListType,

// 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 (bb BlockBlobURL) CopyFromURL(ctx context.Context, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions, srcContentMD5 []byte, tier AccessTierType, blobTagsMap BlobTagsMap, immutability ImmutabilityPolicyOptions) (*BlobCopyFromURLResponse, error) {
func (bb BlockBlobURL) CopyFromURL(ctx context.Context, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions, srcContentMD5 []byte, tier AccessTierType, blobTagsMap BlobTagsMap, immutability ImmutabilityPolicyOptions, sourceAuthorization TokenCredential) (*BlobCopyFromURLResponse, error) {
srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.pointers()
dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.ModifiedAccessConditions.pointers()
dstLeaseID := dstac.LeaseAccessConditions.pointers()
Expand All @@ -161,12 +161,12 @@ func (bb BlockBlobURL) CopyFromURL(ctx context.Context, source url.URL, metadata
dstLeaseID, nil, srcContentMD5,
blobTagsString, // Blob tags
// immutability policy
immutabilityExpiry, immutabilityMode, legalHold, nil)
immutabilityExpiry, immutabilityMode, legalHold, tokenCredentialPointers(sourceAuthorization))
}

// PutBlobFromURL synchronously creates a new Block Blob with data from the source URL up to a max length of 256MB.
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob-from-url.
func (bb BlockBlobURL) PutBlobFromURL(ctx context.Context, h BlobHTTPHeaders, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions, srcContentMD5 []byte, dstContentMD5 []byte, tier AccessTierType, blobTagsMap BlobTagsMap, cpk ClientProvidedKeyOptions) (*BlockBlobPutBlobFromURLResponse, error) {
func (bb BlockBlobURL) PutBlobFromURL(ctx context.Context, h BlobHTTPHeaders, source url.URL, metadata Metadata, srcac ModifiedAccessConditions, dstac BlobAccessConditions, srcContentMD5 []byte, dstContentMD5 []byte, tier AccessTierType, blobTagsMap BlobTagsMap, cpk ClientProvidedKeyOptions, sourceAuthorization TokenCredential) (*BlockBlobPutBlobFromURLResponse, error) {

srcIfModifiedSince, srcIfUnmodifiedSince, srcIfMatchETag, srcIfNoneMatchETag := srcac.pointers()
dstIfModifiedSince, dstIfUnmodifiedSince, dstIfMatchETag, dstIfNoneMatchETag := dstac.ModifiedAccessConditions.pointers()
Expand All @@ -178,5 +178,5 @@ func (bb BlockBlobURL) PutBlobFromURL(ctx context.Context, h BlobHTTPHeaders, so
metadata, dstLeaseID, &h.ContentDisposition, cpk.EncryptionKey, cpk.EncryptionKeySha256,
cpk.EncryptionAlgorithm, cpk.EncryptionScope, tier, dstIfModifiedSince, dstIfUnmodifiedSince,
dstIfMatchETag, dstIfNoneMatchETag, nil, srcIfModifiedSince, srcIfUnmodifiedSince,
srcIfMatchETag, srcIfNoneMatchETag, nil, nil, srcContentMD5, blobTagsString, nil, nil)
srcIfMatchETag, srcIfNoneMatchETag, nil, nil, srcContentMD5, blobTagsString, nil, tokenCredentialPointers(sourceAuthorization))
}
4 changes: 2 additions & 2 deletions azblob/url_page_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (pb PageBlobURL) UploadPages(ctx context.Context, offset int64, body io.Rea
// The destOffset specifies the start offset of data in page blob will be written to.
// The count must be a multiple of 512 bytes.
// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page-from-url.
func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL, sourceOffset int64, destOffset int64, count int64, transactionalMD5 []byte, destinationAccessConditions PageBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, cpk ClientProvidedKeyOptions) (*PageBlobUploadPagesFromURLResponse, error) {
func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL, sourceOffset int64, destOffset int64, count int64, transactionalMD5 []byte, destinationAccessConditions PageBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, cpk ClientProvidedKeyOptions, sourceAuthorization TokenCredential) (*PageBlobUploadPagesFromURLResponse, error) {
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers()
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers()
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := destinationAccessConditions.SequenceNumberAccessConditions.pointers()
Expand All @@ -115,7 +115,7 @@ func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL,
ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual,
ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag,
nil, // Blob ifTags
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, nil)
sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil, tokenCredentialPointers(sourceAuthorization))
}

// ClearPages frees the specified pages from the page blob.
Expand Down
11 changes: 10 additions & 1 deletion azblob/zc_credential_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ type TokenCredential interface {
SetToken(newToken string)
}

func tokenCredentialPointers(credential TokenCredential) *string {
if credential == nil {
return nil
}

out := "Bearer " + credential.Token()
return &out
}

// NewTokenCredential creates a token credential for use with role-based access control (RBAC) access to Azure Storage
// resources. You initialize the TokenCredential with an initial token value. If you pass a non-nil value for
// tokenRefresher, then the function you pass will be called immediately so it can refresh and change the
Expand Down Expand Up @@ -68,7 +77,7 @@ func (f *tokenCredentialWithRefresh) New(next pipeline.Policy, po *pipeline.Poli
return f.token.New(next, po)
}

///////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////

// tokenCredential is a pipeline.Factory is the credential's policy factory.
type tokenCredential struct {
Expand Down
16 changes: 8 additions & 8 deletions azblob/zt_blob_tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,15 @@ func (s *aztestsSuite) TestStageBlockFromURLWithTags(c *chk.C) {
srcBlobURLWithSAS := srcBlobParts.URL()

blockID1, blockID2 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0))), base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 1)))
stageResp1, err := destBlob.StageBlockFromURL(ctx, blockID1, srcBlobURLWithSAS, 0, 4*1024*1024, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
stageResp1, err := destBlob.StageBlockFromURL(ctx, blockID1, srcBlobURLWithSAS, 0, 4*1024*1024, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(stageResp1.Response().StatusCode, chk.Equals, 201)
c.Assert(stageResp1.ContentMD5(), chk.Not(chk.Equals), "")
c.Assert(stageResp1.RequestID(), chk.Not(chk.Equals), "")
c.Assert(stageResp1.Version(), chk.Not(chk.Equals), "")
c.Assert(stageResp1.Date().IsZero(), chk.Equals, false)

stageResp2, err := destBlob.StageBlockFromURL(ctx, blockID2, srcBlobURLWithSAS, 4*1024*1024, CountToEnd, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{})
stageResp2, err := destBlob.StageBlockFromURL(ctx, blockID2, srcBlobURLWithSAS, 4*1024*1024, CountToEnd, LeaseAccessConditions{}, ModifiedAccessConditions{}, ClientProvidedKeyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(stageResp2.Response().StatusCode, chk.Equals, 201)
c.Assert(stageResp2.ContentMD5(), chk.Not(chk.Equals), "")
Expand All @@ -283,7 +283,7 @@ func (s *aztestsSuite) TestStageBlockFromURLWithTags(c *chk.C) {
listResp, err := destBlob.CommitBlockList(ctx, []string{blockID1, blockID2}, BlobHTTPHeaders{}, nil, BlobAccessConditions{}, DefaultAccessTier, blobTagsMap, ClientProvidedKeyOptions{}, ImmutabilityPolicyOptions{})
c.Assert(err, chk.IsNil)
c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
//versionId := listResp.VersionID()
// versionId := listResp.VersionID()

downloadResp, err := destBlob.BlobURL.Download(ctx, 0, CountToEnd, BlobAccessConditions{}, false, ClientProvidedKeyOptions{})
c.Assert(err, chk.IsNil)
Expand Down Expand Up @@ -340,7 +340,7 @@ func (s *aztestsSuite) TestCopyBlockBlobFromURLWithTags(c *chk.C) {

srcBlobURLWithSAS := srcBlobParts.URL()

resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil, ImmutabilityPolicyOptions{})
resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(resp.Response().StatusCode, chk.Equals, 202)
c.Assert(resp.ETag(), chk.Not(chk.Equals), "")
Expand All @@ -360,10 +360,10 @@ func (s *aztestsSuite) TestCopyBlockBlobFromURLWithTags(c *chk.C) {
c.Assert(len(downloadResp.NewMetadata()), chk.Equals, 1)

_, badMD5 := getRandomDataAndReader(16)
_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, blobTagsMap, ImmutabilityPolicyOptions{})
_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, blobTagsMap, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.NotNil)

resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, blobTagsMap, ImmutabilityPolicyOptions{})
resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, blobTagsMap, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(resp.Response().StatusCode, chk.Equals, 202)
c.Assert(resp.XMsContentCrc64(), chk.Not(chk.Equals), "")
Expand Down Expand Up @@ -575,8 +575,8 @@ func (s *aztestsSuite) TestFindBlobsByTags(c *chk.C) {
c.Assert(err, chk.IsNil)
c.Assert(lResp.Blobs, chk.HasLen, 0)

//where = "\"tag1\"='firsttag'AND\"tag2\"='secondtag'AND\"@container\"='"+ containerName1 + "'"
//TODO: Figure out how to do a composite query based on container.
// where = "\"tag1\"='firsttag'AND\"tag2\"='secondtag'AND\"@container\"='"+ containerName1 + "'"
// TODO: Figure out how to do a composite query based on container.
where = "\"tag1\"='firsttag'AND\"tag2\"='secondtag'"

lResp, err = bsu.FindBlobsByTags(ctx, nil, nil, &where, Marker{}, nil)
Expand Down
6 changes: 3 additions & 3 deletions azblob/zt_blob_versioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (s *aztestsSuite) TestCopyBlobFromURLWithSASReturnsVID(c *chk.C) {

srcBlobURLWithSAS := srcBlobParts.URL()

resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil, ImmutabilityPolicyOptions{})
resp, err := destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{"foo": "bar"}, ModifiedAccessConditions{}, BlobAccessConditions{}, sourceDataMD5Value[:], DefaultAccessTier, nil, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(resp.Response().StatusCode, chk.Equals, 202)
c.Assert(resp.Version(), chk.Not(chk.Equals), "")
Expand All @@ -263,10 +263,10 @@ func (s *aztestsSuite) TestCopyBlobFromURLWithSASReturnsVID(c *chk.C) {
c.Assert(downloadResp.Response().Header.Get("x-ms-version-id"), chk.NotNil)
c.Assert(len(downloadResp.NewMetadata()), chk.Equals, 1)
_, badMD5 := getRandomDataAndReader(16)
_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, nil, ImmutabilityPolicyOptions{})
_, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, badMD5, DefaultAccessTier, nil, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.NotNil)

resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, nil, ImmutabilityPolicyOptions{})
resp, err = destBlob.CopyFromURL(ctx, srcBlobURLWithSAS, Metadata{}, ModifiedAccessConditions{}, BlobAccessConditions{}, nil, DefaultAccessTier, nil, ImmutabilityPolicyOptions{}, nil)
c.Assert(err, chk.IsNil)
c.Assert(resp.Response().StatusCode, chk.Equals, 202)
c.Assert(resp.XMsContentCrc64(), chk.Not(chk.Equals), "")
Expand Down

0 comments on commit e7f2275

Please sign in to comment.