From 2d77a002b56cf9092c166633696010ef7178d40d Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 23 Jun 2023 14:47:29 +0530 Subject: [PATCH 01/10] Adding constructor for oauth credentials --- sdk/storage/azfile/directory/client.go | 20 ++++++++++++++++++++ sdk/storage/azfile/file/client.go | 20 ++++++++++++++++++++ sdk/storage/azfile/service/client.go | 20 ++++++++++++++++++++ sdk/storage/azfile/share/client.go | 20 ++++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/sdk/storage/azfile/directory/client.go b/sdk/storage/azfile/directory/client.go index 07a609261282..72236bc721e8 100644 --- a/sdk/storage/azfile/directory/client.go +++ b/sdk/storage/azfile/directory/client.go @@ -28,6 +28,26 @@ type ClientOptions base.ClientOptions // Client represents a URL to the Azure Storage directory allowing you to manipulate its directories and files. type Client base.Client[generated.DirectoryClient] +// NewClient creates an instance of Client with the specified values. +// - directoryURL - the URL of the directory e.g. https://.file.core.windows.net/share/directory +// - cred - an Azure AD credential, typically obtained via the azidentity module +// - options - client options; pass nil to accept the default values +func NewClient(directoryURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { + authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) + conOptions := shared.GetClientOptions(options) + plOpts := runtime.PipelineOptions{ + PerRetry: []policy.Policy{authPolicy}, + } + base.SetPipelineOptions((*base.ClientOptions)(conOptions), &plOpts) + + azClient, err := azcore.NewClient(shared.DirectoryClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) + if err != nil { + return nil, err + } + + return (*Client)(base.NewDirectoryClient(directoryURL, azClient, nil, (*base.ClientOptions)(conOptions))), nil +} + // NewClientWithNoCredential creates an instance of Client with the specified values. // This is used to anonymously access a directory or with a shared access signature (SAS) token. // - directoryURL - the URL of the directory e.g. https://.file.core.windows.net/share/directory? diff --git a/sdk/storage/azfile/file/client.go b/sdk/storage/azfile/file/client.go index 235babcd9653..82b245f6b40f 100644 --- a/sdk/storage/azfile/file/client.go +++ b/sdk/storage/azfile/file/client.go @@ -34,6 +34,26 @@ type ClientOptions base.ClientOptions // Client represents a URL to the Azure Storage file. type Client base.Client[generated.FileClient] +// NewClient creates an instance of Client with the specified values. +// - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file +// - cred - an Azure AD credential, typically obtained via the azidentity module +// - options - client options; pass nil to accept the default values +func NewClient(fileURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { + authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) + conOptions := shared.GetClientOptions(options) + plOpts := runtime.PipelineOptions{ + PerRetry: []policy.Policy{authPolicy}, + } + base.SetPipelineOptions((*base.ClientOptions)(conOptions), &plOpts) + + azClient, err := azcore.NewClient(shared.FileClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) + if err != nil { + return nil, err + } + + return (*Client)(base.NewFileClient(fileURL, azClient, nil, (*base.ClientOptions)(conOptions))), nil +} + // NewClientWithNoCredential creates an instance of Client with the specified values. // This is used to anonymously access a file or with a shared access signature (SAS) token. // - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file? diff --git a/sdk/storage/azfile/service/client.go b/sdk/storage/azfile/service/client.go index 372313f78eb5..210d7277e1af 100644 --- a/sdk/storage/azfile/service/client.go +++ b/sdk/storage/azfile/service/client.go @@ -30,6 +30,26 @@ type ClientOptions base.ClientOptions // Client represents a URL to the Azure File Storage service allowing you to manipulate file shares. type Client base.Client[generated.ServiceClient] +// NewClient creates an instance of Client with the specified values. +// - serviceURL - the URL of the storage account e.g. https://.file.core.windows.net/ +// - cred - an Azure AD credential, typically obtained via the azidentity module +// - options - client options; pass nil to accept the default values +func NewClient(serviceURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { + authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) + conOptions := shared.GetClientOptions(options) + plOpts := runtime.PipelineOptions{ + PerRetry: []policy.Policy{authPolicy}, + } + base.SetPipelineOptions((*base.ClientOptions)(conOptions), &plOpts) + + azClient, err := azcore.NewClient(shared.ServiceClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) + if err != nil { + return nil, err + } + + return (*Client)(base.NewServiceClient(serviceURL, azClient, nil, (*base.ClientOptions)(conOptions))), nil +} + // NewClientWithNoCredential creates an instance of Client with the specified values. // This is used to anonymously access a storage account or with a shared access signature (SAS) token. // - serviceURL - the URL of the storage account e.g. https://.file.core.windows.net/? diff --git a/sdk/storage/azfile/share/client.go b/sdk/storage/azfile/share/client.go index 4f797404cfbb..536ca31e5b34 100644 --- a/sdk/storage/azfile/share/client.go +++ b/sdk/storage/azfile/share/client.go @@ -30,6 +30,26 @@ type ClientOptions base.ClientOptions // Client represents a URL to the Azure Storage share allowing you to manipulate its directories and files. type Client base.Client[generated.ShareClient] +// NewClient creates an instance of Client with the specified values. +// - shareURL - the URL of the share e.g. https://.file.core.windows.net/share +// - cred - an Azure AD credential, typically obtained via the azidentity module +// - options - client options; pass nil to accept the default values +func NewClient(shareURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { + authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) + conOptions := shared.GetClientOptions(options) + plOpts := runtime.PipelineOptions{ + PerRetry: []policy.Policy{authPolicy}, + } + base.SetPipelineOptions((*base.ClientOptions)(conOptions), &plOpts) + + azClient, err := azcore.NewClient(shared.ShareClient, exported.ModuleVersion, plOpts, &conOptions.ClientOptions) + if err != nil { + return nil, err + } + + return (*Client)(base.NewShareClient(shareURL, azClient, nil, (*base.ClientOptions)(conOptions))), nil +} + // NewClientWithNoCredential creates an instance of Client with the specified values. // This is used to anonymously access a share or with a shared access signature (SAS) token. // - shareURL - the URL of the share e.g. https://.file.core.windows.net/share? From 202229e84d679443ca7c72c1d1b9a9ad946042be Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 23 Jun 2023 15:15:12 +0530 Subject: [PATCH 02/10] Adding dir create test --- sdk/storage/azfile/directory/client_test.go | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/sdk/storage/azfile/directory/client_test.go b/sdk/storage/azfile/directory/client_test.go index 0403ffd2859a..4224f890a508 100644 --- a/sdk/storage/azfile/directory/client_test.go +++ b/sdk/storage/azfile/directory/client_test.go @@ -10,6 +10,7 @@ import ( "context" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/directory" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" @@ -140,6 +141,45 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { _require.Equal(resp.FileChangeTime.IsZero(), false) } +func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.NoError(err) + _require.NotNil(resp.ETag) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) + _require.Equal(resp.FileLastWriteTime.IsZero(), false) + _require.Equal(resp.FileChangeTime.IsZero(), false) + + gResp, err := dirClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(gResp) +} + func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingConnectionString() { _require := require.New(d.T()) testName := d.T().Name() From 770739e9a3a331960d97244b060893aea1e540e4 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Tue, 27 Jun 2023 16:32:13 +0530 Subject: [PATCH 03/10] Adding tests in directory package --- sdk/storage/azfile/directory/client_test.go | 375 ++++++++++++++++++-- 1 file changed, 336 insertions(+), 39 deletions(-) diff --git a/sdk/storage/azfile/directory/client_test.go b/sdk/storage/azfile/directory/client_test.go index 4224f890a508..0b0f3831cf03 100644 --- a/sdk/storage/azfile/directory/client_test.go +++ b/sdk/storage/azfile/directory/client_test.go @@ -141,45 +141,6 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingSharedKey() { _require.Equal(resp.FileChangeTime.IsZero(), false) } -func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingOAuth() { - _require := require.New(d.T()) - testName := d.T().Name() - - accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) - _require.Greater(len(accountName), 0) - - cred, err := azidentity.NewDefaultAzureCredential(nil) - _require.NoError(err) - - svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) - _require.NoError(err) - - shareName := testcommon.GenerateShareName(testName) - shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) - defer testcommon.DeleteShare(context.Background(), _require, shareClient) - - dirName := testcommon.GenerateDirectoryName(testName) - dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName - - options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} - testcommon.SetClientOptions(d.T(), &options.ClientOptions) - dirClient, err := directory.NewClient(dirURL, cred, options) - _require.NoError(err) - - resp, err := dirClient.Create(context.Background(), nil) - _require.NoError(err) - _require.NotNil(resp.ETag) - _require.NotNil(resp.RequestID) - _require.Equal(resp.LastModified.IsZero(), false) - _require.Equal(resp.FileCreationTime.IsZero(), false) - _require.Equal(resp.FileLastWriteTime.IsZero(), false) - _require.Equal(resp.FileChangeTime.IsZero(), false) - - gResp, err := dirClient.GetProperties(context.Background(), nil) - _require.NoError(err) - _require.NotNil(gResp) -} - func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateUsingConnectionString() { _require := require.New(d.T()) testName := d.T().Name() @@ -1212,3 +1173,339 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateWithTrailingSlash() { _, err = subDirClient.Create(context.Background(), nil) _require.NoError(err) } + +func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateDeleteUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + resp, err := dirClient.Create(context.Background(), nil) + _require.NoError(err) + _require.NotNil(resp.ETag) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) + _require.Equal(resp.FileLastWriteTime.IsZero(), false) + _require.Equal(resp.FileChangeTime.IsZero(), false) + + gResp, err := dirClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(gResp.FileCreationTime) + _require.NotNil(gResp.FileLastWriteTime) + _require.NotNil(gResp.FilePermissionKey) + + dResp, err := dirClient.Delete(context.Background(), nil) + _require.NoError(err) + _require.Equal(dResp.Date.IsZero(), false) + _require.NotNil(dResp.RequestID) + _require.NotNil(dResp.Version) + + _, err = dirClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) +} + +func (d *DirectoryRecordedTestsSuite) TestDirectorySetPropertiesUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + cResp, err := dirClient.Create(context.Background(), nil) + _require.NoError(err) + _require.NotNil(cResp.FilePermissionKey) + + currTime, err := time.Parse(time.UnixDate, "Fri Mar 31 21:00:00 GMT 2023") + _require.NoError(err) + creationTime := currTime.Add(5 * time.Minute).Round(time.Microsecond) + lastWriteTime := currTime.Add(10 * time.Minute).Round(time.Millisecond) + + // Set the custom permissions + sResp, err := dirClient.SetProperties(context.Background(), &directory.SetPropertiesOptions{ + FileSMBProperties: &file.SMBProperties{ + Attributes: &file.NTFSFileAttributes{ + ReadOnly: true, + System: true, + }, + CreationTime: &creationTime, + LastWriteTime: &lastWriteTime, + }, + FilePermissions: &file.Permissions{ + Permission: &testcommon.SampleSDDL, + }, + }) + _require.NoError(err) + _require.NotNil(sResp.FileCreationTime) + _require.NotNil(sResp.FileLastWriteTime) + _require.NotNil(sResp.FilePermissionKey) + _require.NotEqual(*sResp.FilePermissionKey, *cResp.FilePermissionKey) + _require.Equal(*sResp.FileCreationTime, creationTime.UTC()) + _require.Equal(*sResp.FileLastWriteTime, lastWriteTime.UTC()) + + fileAttributes, err := file.ParseNTFSFileAttributes(sResp.FileAttributes) + _require.NoError(err) + _require.NotNil(fileAttributes) + _require.True(fileAttributes.ReadOnly) + _require.True(fileAttributes.System) + _require.True(fileAttributes.Directory) + + gResp, err := dirClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(gResp.FileCreationTime) + _require.NotNil(gResp.FileLastWriteTime) + _require.NotNil(gResp.FilePermissionKey) + _require.Equal(*gResp.FilePermissionKey, *sResp.FilePermissionKey) + _require.Equal(*gResp.FileCreationTime, *sResp.FileCreationTime) + _require.Equal(*gResp.FileLastWriteTime, *sResp.FileLastWriteTime) + _require.Equal(*gResp.FileAttributes, *sResp.FileAttributes) + + fileAttributes2, err := file.ParseNTFSFileAttributes(gResp.FileAttributes) + _require.NoError(err) + _require.NotNil(fileAttributes2) + _require.True(fileAttributes2.ReadOnly) + _require.True(fileAttributes2.System) + _require.True(fileAttributes2.Directory) + _require.EqualValues(fileAttributes2, fileAttributes) +} + +func (d *DirectoryRecordedTestsSuite) TestDirectorySetMetadataUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + _, err = dirClient.Create(context.Background(), nil) + _require.NoError(err) + + md := map[string]*string{ + "Foo": to.Ptr("FooValuE"), + "Bar": to.Ptr("bArvaLue"), + } + + sResp, err := dirClient.SetMetadata(context.Background(), &directory.SetMetadataOptions{ + Metadata: md, + }) + _require.NoError(err) + _require.Equal(sResp.Date.IsZero(), false) + _require.NotNil(sResp.ETag) + _require.NotNil(sResp.RequestID) + _require.NotNil(sResp.Version) + _require.NotNil(sResp.IsServerEncrypted) + + gResp, err := dirClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(gResp.Date.IsZero(), false) + _require.NotNil(gResp.ETag) + _require.Equal(gResp.LastModified.IsZero(), false) + _require.NotNil(gResp.RequestID) + _require.NotNil(gResp.Version) + _require.NotNil(gResp.IsServerEncrypted) + _require.EqualValues(gResp.Metadata, md) +} + +func (d *DirectoryRecordedTestsSuite) TestDirectoryListHandlesUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + _, err = dirClient.Create(context.Background(), nil) + _require.NoError(err) + + resp, err := dirClient.ListHandles(context.Background(), nil) + _require.NoError(err) + _require.Len(resp.Handles, 0) + _require.NotNil(resp.NextMarker) + _require.Equal(*resp.NextMarker, "") +} + +func (d *DirectoryRecordedTestsSuite) TestDirectoryForceCloseHandlesUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + _, err = dirClient.Create(context.Background(), nil) + _require.NoError(err) + + resp, err := dirClient.ForceCloseHandles(context.Background(), "*", nil) + _require.NoError(err) + _require.EqualValues(*resp.NumberOfHandlesClosed, 0) + _require.EqualValues(*resp.NumberOfHandlesFailedToClose, 0) + _require.Nil(resp.Marker) +} + +func (d *DirectoryRecordedTestsSuite) TestDirectoryListUsingOAuth() { + _require := require.New(d.T()) + testName := d.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := azidentity.NewDefaultAzureCredential(nil) + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + dirName := testcommon.GenerateDirectoryName(testName) + dirURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + dirName + + options := &directory.ClientOptions{FileRequestIntent: to.Ptr(directory.ShareTokenIntentBackup)} + testcommon.SetClientOptions(d.T(), &options.ClientOptions) + dirClient, err := directory.NewClient(dirURL, cred, options) + _require.NoError(err) + + _, err = dirClient.Create(context.Background(), nil) + _require.NoError(err) + + fileName := testcommon.GenerateFileName(testName) + + // create directories + for i := 0; i < 10; i++ { + subDirClient := dirClient.NewSubdirectoryClient(dirName + fmt.Sprintf("%v", i)) + _, err = subDirClient.Create(context.Background(), nil) + _require.NoError(err) + } + + // create files + for i := 0; i < 5; i++ { + fileClient := dirClient.NewFileClient(fileName + fmt.Sprintf("%v", i)) + _, err = fileClient.Create(context.Background(), 2048, nil) + _require.NoError(err) + } + + subDirCtr, fileCtr := 0, 0 + pager := dirClient.NewListFilesAndDirectoriesPager(nil) + for pager.More() { + resp, err := pager.NextPage(context.Background()) + _require.NoError(err) + subDirCtr += len(resp.Segment.Directories) + fileCtr += len(resp.Segment.Files) + for _, dir := range resp.Segment.Directories { + _require.NotNil(dir.Name) + _require.Greater(len(*dir.Name), 0) + _require.NotNil(dir.ID) + _require.Nil(dir.Attributes) + _require.Nil(dir.PermissionKey) + _require.Nil(dir.Properties.ETag) + _require.Nil(dir.Properties.ChangeTime) + _require.Nil(dir.Properties.CreationTime) + _require.Nil(dir.Properties.ContentLength) + } + for _, f := range resp.Segment.Files { + _require.NotNil(f.Name) + _require.Greater(len(*f.Name), 0) + _require.NotNil(f.ID) + _require.Nil(f.Attributes) + _require.Nil(f.PermissionKey) + _require.Nil(f.Properties.ETag) + _require.Nil(f.Properties.ChangeTime) + _require.Nil(f.Properties.CreationTime) + _require.NotNil(f.Properties.ContentLength) + _require.Equal(*f.Properties.ContentLength, int64(2048)) + } + } + _require.Equal(subDirCtr, 10) + _require.Equal(fileCtr, 5) +} From d59144e32f13e892b93381c717ae1acd2f29d1f0 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Tue, 27 Jun 2023 19:25:49 +0530 Subject: [PATCH 04/10] Adding recordings for directory tests --- sdk/storage/azfile/assets.json | 2 +- sdk/storage/azfile/directory/client.go | 1 + sdk/storage/azfile/directory/client_test.go | 13 ++++++------- sdk/storage/azfile/file/client.go | 1 + sdk/storage/azfile/service/client.go | 3 +++ sdk/storage/azfile/share/client.go | 2 ++ 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sdk/storage/azfile/assets.json b/sdk/storage/azfile/assets.json index 297645fadc04..65d65c3725c0 100644 --- a/sdk/storage/azfile/assets.json +++ b/sdk/storage/azfile/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/storage/azfile", - "Tag": "go/storage/azfile_600a15563c" + "Tag": "go/storage/azfile_5efb4aa738" } diff --git a/sdk/storage/azfile/directory/client.go b/sdk/storage/azfile/directory/client.go index 72236bc721e8..24da6b41128a 100644 --- a/sdk/storage/azfile/directory/client.go +++ b/sdk/storage/azfile/directory/client.go @@ -32,6 +32,7 @@ type Client base.Client[generated.DirectoryClient] // - directoryURL - the URL of the directory e.g. https://.file.core.windows.net/share/directory // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// Note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(directoryURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) conOptions := shared.GetClientOptions(options) diff --git a/sdk/storage/azfile/directory/client_test.go b/sdk/storage/azfile/directory/client_test.go index 0b0f3831cf03..b08168317720 100644 --- a/sdk/storage/azfile/directory/client_test.go +++ b/sdk/storage/azfile/directory/client_test.go @@ -10,7 +10,6 @@ import ( "context" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/directory" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" @@ -1181,7 +1180,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryCreateDeleteUsingOAuth() { accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -1232,7 +1231,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectorySetPropertiesUsingOAuth() { accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -1314,7 +1313,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectorySetMetadataUsingOAuth() { accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -1368,7 +1367,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryListHandlesUsingOAuth() { accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -1403,7 +1402,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryForceCloseHandlesUsingOAuth() accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) @@ -1438,7 +1437,7 @@ func (d *DirectoryRecordedTestsSuite) TestDirectoryListUsingOAuth() { accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) _require.Greater(len(accountName), 0) - cred, err := azidentity.NewDefaultAzureCredential(nil) + cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) svcClient, err := testcommon.GetServiceClient(d.T(), testcommon.TestAccountDefault, nil) diff --git a/sdk/storage/azfile/file/client.go b/sdk/storage/azfile/file/client.go index 82b245f6b40f..ccc21fb6f842 100644 --- a/sdk/storage/azfile/file/client.go +++ b/sdk/storage/azfile/file/client.go @@ -38,6 +38,7 @@ type Client base.Client[generated.FileClient] // - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// Note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(fileURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) conOptions := shared.GetClientOptions(options) diff --git a/sdk/storage/azfile/service/client.go b/sdk/storage/azfile/service/client.go index 210d7277e1af..64de91aeebc0 100644 --- a/sdk/storage/azfile/service/client.go +++ b/sdk/storage/azfile/service/client.go @@ -34,6 +34,9 @@ type Client base.Client[generated.ServiceClient] // - serviceURL - the URL of the storage account e.g. https://.file.core.windows.net/ // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// Note that service-level operations do not support token credential authentication. +// This constructor exists to allow the construction of a share.Client that has token credential authentication. +// Also note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(serviceURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) conOptions := shared.GetClientOptions(options) diff --git a/sdk/storage/azfile/share/client.go b/sdk/storage/azfile/share/client.go index 536ca31e5b34..ac2c9d36bc3d 100644 --- a/sdk/storage/azfile/share/client.go +++ b/sdk/storage/azfile/share/client.go @@ -34,6 +34,8 @@ type Client base.Client[generated.ShareClient] // - shareURL - the URL of the share e.g. https://.file.core.windows.net/share // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// Note that the only share-level operations that support token credential authentication are CreatePermission and GetPermission. +// Also note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(shareURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) conOptions := shared.GetClientOptions(options) From 8ca50e2d9b33f43c9dacce4a0c06346e393492a3 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Thu, 29 Jun 2023 10:48:50 +0530 Subject: [PATCH 05/10] Adding tests for service and share clients --- sdk/storage/azfile/fileerror/error_codes.go | 131 ++++++++++---------- sdk/storage/azfile/service/client_test.go | 62 +++++++++ sdk/storage/azfile/share/client_test.go | 65 ++++++++++ 3 files changed, 193 insertions(+), 65 deletions(-) diff --git a/sdk/storage/azfile/fileerror/error_codes.go b/sdk/storage/azfile/fileerror/error_codes.go index c897c0953828..5b6070f0a990 100644 --- a/sdk/storage/azfile/fileerror/error_codes.go +++ b/sdk/storage/azfile/fileerror/error_codes.go @@ -34,71 +34,72 @@ func HasCode(err error, codes ...Code) bool { type Code = generated.StorageErrorCode const ( - AccountAlreadyExists Code = "AccountAlreadyExists" - AccountBeingCreated Code = "AccountBeingCreated" - AccountIsDisabled Code = "AccountIsDisabled" - AuthenticationFailed Code = "AuthenticationFailed" - AuthorizationFailure Code = "AuthorizationFailure" - AuthorizationPermissionMismatch Code = "AuthorizationPermissionMismatch" - AuthorizationProtocolMismatch Code = "AuthorizationProtocolMismatch" - AuthorizationResourceTypeMismatch Code = "AuthorizationResourceTypeMismatch" - AuthorizationServiceMismatch Code = "AuthorizationServiceMismatch" - AuthorizationSourceIPMismatch Code = "AuthorizationSourceIPMismatch" - CannotDeleteFileOrDirectory Code = "CannotDeleteFileOrDirectory" - ClientCacheFlushDelay Code = "ClientCacheFlushDelay" - ConditionHeadersNotSupported Code = "ConditionHeadersNotSupported" - ConditionNotMet Code = "ConditionNotMet" - DeletePending Code = "DeletePending" - DirectoryNotEmpty Code = "DirectoryNotEmpty" - EmptyMetadataKey Code = "EmptyMetadataKey" - FeatureVersionMismatch Code = "FeatureVersionMismatch" - FileLockConflict Code = "FileLockConflict" - InsufficientAccountPermissions Code = "InsufficientAccountPermissions" - InternalError Code = "InternalError" - InvalidAuthenticationInfo Code = "InvalidAuthenticationInfo" - InvalidFileOrDirectoryPathName Code = "InvalidFileOrDirectoryPathName" - InvalidHTTPVerb Code = "InvalidHttpVerb" - InvalidHeaderValue Code = "InvalidHeaderValue" - InvalidInput Code = "InvalidInput" - InvalidMD5 Code = "InvalidMd5" - InvalidMetadata Code = "InvalidMetadata" - InvalidQueryParameterValue Code = "InvalidQueryParameterValue" - InvalidRange Code = "InvalidRange" - InvalidResourceName Code = "InvalidResourceName" - InvalidURI Code = "InvalidUri" - InvalidXMLDocument Code = "InvalidXmlDocument" - InvalidXMLNodeValue Code = "InvalidXmlNodeValue" - MD5Mismatch Code = "Md5Mismatch" - MetadataTooLarge Code = "MetadataTooLarge" - MissingContentLengthHeader Code = "MissingContentLengthHeader" - MissingRequiredHeader Code = "MissingRequiredHeader" - MissingRequiredQueryParameter Code = "MissingRequiredQueryParameter" - MissingRequiredXMLNode Code = "MissingRequiredXmlNode" - MultipleConditionHeadersNotSupported Code = "MultipleConditionHeadersNotSupported" - OperationTimedOut Code = "OperationTimedOut" - OutOfRangeInput Code = "OutOfRangeInput" - OutOfRangeQueryParameterValue Code = "OutOfRangeQueryParameterValue" - ParentNotFound Code = "ParentNotFound" - ReadOnlyAttribute Code = "ReadOnlyAttribute" - RequestBodyTooLarge Code = "RequestBodyTooLarge" - RequestURLFailedToParse Code = "RequestUrlFailedToParse" - ResourceAlreadyExists Code = "ResourceAlreadyExists" - ResourceNotFound Code = "ResourceNotFound" - ResourceTypeMismatch Code = "ResourceTypeMismatch" - ServerBusy Code = "ServerBusy" - ShareAlreadyExists Code = "ShareAlreadyExists" - ShareBeingDeleted Code = "ShareBeingDeleted" - ShareDisabled Code = "ShareDisabled" - ShareHasSnapshots Code = "ShareHasSnapshots" - ShareNotFound Code = "ShareNotFound" - ShareSnapshotCountExceeded Code = "ShareSnapshotCountExceeded" - ShareSnapshotInProgress Code = "ShareSnapshotInProgress" - ShareSnapshotOperationNotSupported Code = "ShareSnapshotOperationNotSupported" - SharingViolation Code = "SharingViolation" - UnsupportedHTTPVerb Code = "UnsupportedHttpVerb" - UnsupportedHeader Code = "UnsupportedHeader" - UnsupportedQueryParameter Code = "UnsupportedQueryParameter" - UnsupportedXMLNode Code = "UnsupportedXmlNode" + AccountAlreadyExists Code = "AccountAlreadyExists" + AccountBeingCreated Code = "AccountBeingCreated" + AccountIsDisabled Code = "AccountIsDisabled" + AuthenticationFailed Code = "AuthenticationFailed" + AuthorizationFailure Code = "AuthorizationFailure" + AuthorizationPermissionMismatch Code = "AuthorizationPermissionMismatch" + AuthorizationProtocolMismatch Code = "AuthorizationProtocolMismatch" + AuthorizationResourceTypeMismatch Code = "AuthorizationResourceTypeMismatch" + AuthorizationServiceMismatch Code = "AuthorizationServiceMismatch" + AuthorizationSourceIPMismatch Code = "AuthorizationSourceIPMismatch" + CannotDeleteFileOrDirectory Code = "CannotDeleteFileOrDirectory" + ClientCacheFlushDelay Code = "ClientCacheFlushDelay" + ConditionHeadersNotSupported Code = "ConditionHeadersNotSupported" + ConditionNotMet Code = "ConditionNotMet" + DeletePending Code = "DeletePending" + DirectoryNotEmpty Code = "DirectoryNotEmpty" + EmptyMetadataKey Code = "EmptyMetadataKey" + FeatureVersionMismatch Code = "FeatureVersionMismatch" + FileLockConflict Code = "FileLockConflict" + InsufficientAccountPermissions Code = "InsufficientAccountPermissions" + InternalError Code = "InternalError" + InvalidAuthenticationInfo Code = "InvalidAuthenticationInfo" + InvalidFileOrDirectoryPathName Code = "InvalidFileOrDirectoryPathName" + InvalidHTTPVerb Code = "InvalidHttpVerb" + InvalidHeaderValue Code = "InvalidHeaderValue" + InvalidInput Code = "InvalidInput" + InvalidMD5 Code = "InvalidMd5" + InvalidMetadata Code = "InvalidMetadata" + InvalidQueryParameterValue Code = "InvalidQueryParameterValue" + InvalidRange Code = "InvalidRange" + InvalidResourceName Code = "InvalidResourceName" + InvalidURI Code = "InvalidUri" + InvalidXMLDocument Code = "InvalidXmlDocument" + InvalidXMLNodeValue Code = "InvalidXmlNodeValue" + MD5Mismatch Code = "Md5Mismatch" + MetadataTooLarge Code = "MetadataTooLarge" + MissingContentLengthHeader Code = "MissingContentLengthHeader" + MissingRequiredHeader Code = "MissingRequiredHeader" + MissingRequiredQueryParameter Code = "MissingRequiredQueryParameter" + MissingRequiredXMLNode Code = "MissingRequiredXmlNode" + MultipleConditionHeadersNotSupported Code = "MultipleConditionHeadersNotSupported" + OperationTimedOut Code = "OperationTimedOut" + OutOfRangeInput Code = "OutOfRangeInput" + OutOfRangeQueryParameterValue Code = "OutOfRangeQueryParameterValue" + ParentNotFound Code = "ParentNotFound" + ReadOnlyAttribute Code = "ReadOnlyAttribute" + RequestBodyTooLarge Code = "RequestBodyTooLarge" + RequestURLFailedToParse Code = "RequestUrlFailedToParse" + ResourceAlreadyExists Code = "ResourceAlreadyExists" + ResourceNotFound Code = "ResourceNotFound" + ResourceTypeMismatch Code = "ResourceTypeMismatch" + ServerBusy Code = "ServerBusy" + ShareAlreadyExists Code = "ShareAlreadyExists" + ShareBeingDeleted Code = "ShareBeingDeleted" + ShareDisabled Code = "ShareDisabled" + ShareHasSnapshots Code = "ShareHasSnapshots" + ShareNotFound Code = "ShareNotFound" + ShareSnapshotCountExceeded Code = "ShareSnapshotCountExceeded" + ShareSnapshotInProgress Code = "ShareSnapshotInProgress" + ShareSnapshotOperationNotSupported Code = "ShareSnapshotOperationNotSupported" + SharingViolation Code = "SharingViolation" + UnsupportedHTTPVerb Code = "UnsupportedHttpVerb" + UnsupportedHeader Code = "UnsupportedHeader" + UnsupportedQueryParameter Code = "UnsupportedQueryParameter" + UnsupportedXMLNode Code = "UnsupportedXmlNode" + FileOAuthManagementApiRestrictedToSrp Code = "FileOAuthManagementApiRestrictedToSrp" ) var ( diff --git a/sdk/storage/azfile/service/client_test.go b/sdk/storage/azfile/service/client_test.go index d9c3642c4628..0b1e6240e12b 100644 --- a/sdk/storage/azfile/service/client_test.go +++ b/sdk/storage/azfile/service/client_test.go @@ -452,3 +452,65 @@ func (s *ServiceRecordedTestsSuite) TestServiceCreateDeleteRestoreShare() { } _require.Equal(sharesCnt, 1) } + +func (s *ServiceRecordedTestsSuite) TestServiceOAuthNegative() { + _require := require.New(s.T()) + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := testcommon.GetGenericTokenCredential() + _require.NoError(err) + + options := &service.ClientOptions{FileRequestIntent: to.Ptr(service.ShareTokenIntentBackup)} + svcClient, err := service.NewClient("https://"+accountName+".file.core.windows.net/", cred, options) + _require.NoError(err) + + // service-level operations are not supported using token credential authentication. + _, err = svcClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) + + _, err = svcClient.SetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) + + pager := svcClient.NewListSharesPager(nil) + _, err = pager.NextPage(context.Background()) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) +} + +func (s *ServiceRecordedTestsSuite) TestServiceCreateDeleteDirOAuth() { + _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 := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + // create service client using token credential + options := &service.ClientOptions{FileRequestIntent: to.Ptr(service.ShareTokenIntentBackup)} + svcClientOAuth, err := service.NewClient("https://"+accountName+".file.core.windows.net/", cred, options) + _require.NoError(err) + + dirClient := svcClientOAuth.NewShareClient(shareName).NewDirectoryClient(testcommon.GenerateDirectoryName(testName)) + + _, err = dirClient.Create(context.Background(), nil) + _require.NoError(err) + + _, err = dirClient.GetProperties(context.Background(), nil) + _require.NoError(err) + + _, err = dirClient.Delete(context.Background(), nil) + _require.NoError(err) +} diff --git a/sdk/storage/azfile/share/client_test.go b/sdk/storage/azfile/share/client_test.go index 44940a537d27..18cf3fd6ba2a 100644 --- a/sdk/storage/azfile/share/client_test.go +++ b/sdk/storage/azfile/share/client_test.go @@ -1458,3 +1458,68 @@ func (s *ShareRecordedTestsSuite) TestSASShareClientSignNegative() { _, err = shareClient.GetSASURL(sas.SharePermissions{}, expiry, nil) _require.Equal(err.Error(), "service SAS is missing at least one of these: ExpiryTime or Permissions") } + +func (s *ShareRecordedTestsSuite) TestShareOAuthNegative() { + _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) + + options := &share.ClientOptions{FileRequestIntent: to.Ptr(share.TokenIntentBackup)} + shareName := testcommon.GenerateShareName(testName) + shareClient, err := share.NewClient("https://"+accountName+".file.core.windows.net/"+shareName, cred, options) + _require.NoError(err) + + _, err = shareClient.Create(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) + + _, err = shareClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) + + _, err = shareClient.SetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) + + _, err = shareClient.Delete(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.FileOAuthManagementApiRestrictedToSrp) +} + +func (s *ShareRecordedTestsSuite) TestShareCreateAndGetPermissionOAuth() { + _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 := testcommon.GetServiceClient(s.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + options := &share.ClientOptions{FileRequestIntent: to.Ptr(share.TokenIntentBackup)} + shareClientOAuth, err := share.NewClient("https://"+accountName+".file.core.windows.net/"+shareName, cred, options) + _require.NoError(err) + + // Create a permission and check that it's not empty. + createResp, err := shareClientOAuth.CreatePermission(context.Background(), testcommon.SampleSDDL, nil) + _require.NoError(err) + _require.NotNil(createResp.FilePermissionKey) + _require.NotEmpty(*createResp.FilePermissionKey) + + getResp, err := shareClientOAuth.GetPermission(context.Background(), *createResp.FilePermissionKey, nil) + _require.NoError(err) + _require.NotNil(getResp.Permission) + _require.NotEmpty(*getResp.Permission) +} From 253326955beac1dc69b5d4f96baca53d811e90ad Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 30 Jun 2023 11:21:53 +0530 Subject: [PATCH 06/10] Adding tests for file client --- sdk/storage/azfile/assets.json | 2 +- sdk/storage/azfile/file/client_test.go | 237 ++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 2 deletions(-) diff --git a/sdk/storage/azfile/assets.json b/sdk/storage/azfile/assets.json index 65d65c3725c0..e15822bae44a 100644 --- a/sdk/storage/azfile/assets.json +++ b/sdk/storage/azfile/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/storage/azfile", - "Tag": "go/storage/azfile_5efb4aa738" + "Tag": "go/storage/azfile_e512da82e9" } diff --git a/sdk/storage/azfile/file/client_test.go b/sdk/storage/azfile/file/client_test.go index 6b86ae745325..1e552d43989e 100644 --- a/sdk/storage/azfile/file/client_test.go +++ b/sdk/storage/azfile/file/client_test.go @@ -488,7 +488,6 @@ func (f *FileRecordedTestsSuite) TestFileGetSetPropertiesDefault() { }) _require.NoError(err) - // get properties on the share snapshot getResp, err := fClient.GetProperties(context.Background(), nil) _require.NoError(err) _require.Equal(setResp.LastModified.IsZero(), false) @@ -3152,4 +3151,240 @@ func (f *FileRecordedTestsSuite) TestFileForceCloseHandlesDefault() { _require.Nil(resp.Marker) } +func (f *FileRecordedTestsSuite) TestFileCreateDeleteUsingOAuth() { + _require := require.New(f.T()) + testName := f.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := testcommon.GetGenericTokenCredential() + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fileName := testcommon.GenerateFileName(testName) + fileURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + fileName + + options := &file.ClientOptions{FileRequestIntent: to.Ptr(file.ShareTokenIntentBackup)} + testcommon.SetClientOptions(f.T(), &options.ClientOptions) + fileClient, err := file.NewClient(fileURL, cred, options) + _require.NoError(err) + + resp, err := fileClient.Create(context.Background(), 2048, nil) + _require.NoError(err) + _require.NotNil(resp.ETag) + _require.NotNil(resp.RequestID) + _require.Equal(resp.LastModified.IsZero(), false) + _require.Equal(resp.FileCreationTime.IsZero(), false) + _require.Equal(resp.FileLastWriteTime.IsZero(), false) + _require.Equal(resp.FileChangeTime.IsZero(), false) + + gResp, err := fileClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.NotNil(gResp.FileCreationTime) + _require.NotNil(gResp.FileLastWriteTime) + _require.NotNil(gResp.FilePermissionKey) + _require.Equal(*gResp.ContentLength, int64(2048)) + + dResp, err := fileClient.Delete(context.Background(), nil) + _require.NoError(err) + _require.Equal(dResp.Date.IsZero(), false) + _require.NotNil(dResp.RequestID) + _require.NotNil(dResp.Version) + + _, err = fileClient.GetProperties(context.Background(), nil) + _require.Error(err) + testcommon.ValidateFileErrorCode(_require, err, fileerror.ResourceNotFound) +} + +func (f *FileRecordedTestsSuite) TestFileGetSetPropertiesUsingOAuth() { + _require := require.New(f.T()) + testName := f.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := testcommon.GetGenericTokenCredential() + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fileName := testcommon.GenerateFileName(testName) + fileURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + fileName + + clOptions := &file.ClientOptions{FileRequestIntent: to.Ptr(file.ShareTokenIntentBackup)} + testcommon.SetClientOptions(f.T(), &clOptions.ClientOptions) + fClient, err := file.NewClient(fileURL, cred, clOptions) + _require.NoError(err) + + _, err = fClient.Create(context.Background(), 2048, nil) + _require.NoError(err) + + md5Str := "MDAwMDAwMDA=" + testMd5 := []byte(md5Str) + + options := &file.SetHTTPHeadersOptions{ + Permissions: &file.Permissions{Permission: &testcommon.SampleSDDL}, + SMBProperties: &file.SMBProperties{ + Attributes: &file.NTFSFileAttributes{Hidden: true, ReadOnly: true}, + }, + HTTPHeaders: &file.HTTPHeaders{ + ContentType: to.Ptr("text/html"), + ContentEncoding: to.Ptr("gzip"), + ContentLanguage: to.Ptr("en"), + ContentMD5: testMd5, + CacheControl: to.Ptr("no-transform"), + ContentDisposition: to.Ptr("attachment"), + }, + } + setResp, err := fClient.SetHTTPHeaders(context.Background(), options) + _require.NoError(err) + _require.NotNil(setResp.ETag) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.NotNil(setResp.RequestID) + _require.NotNil(setResp.Version) + _require.Equal(setResp.Date.IsZero(), false) + _require.NotNil(setResp.IsServerEncrypted) + + fileAttributes, err := file.ParseNTFSFileAttributes(setResp.FileAttributes) + _require.NoError(err) + _require.NotNil(fileAttributes) + _require.True(fileAttributes.Hidden) + _require.True(fileAttributes.ReadOnly) + + getResp, err := fClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(setResp.LastModified.IsZero(), false) + _require.Equal(*getResp.FileType, "File") + + _require.EqualValues(getResp.ContentType, options.HTTPHeaders.ContentType) + _require.EqualValues(getResp.ContentEncoding, options.HTTPHeaders.ContentEncoding) + _require.EqualValues(getResp.ContentLanguage, options.HTTPHeaders.ContentLanguage) + _require.EqualValues(getResp.ContentMD5, options.HTTPHeaders.ContentMD5) + _require.EqualValues(getResp.CacheControl, options.HTTPHeaders.CacheControl) + _require.EqualValues(getResp.ContentDisposition, options.HTTPHeaders.ContentDisposition) + _require.Equal(*getResp.ContentLength, int64(2048)) + _require.NotEqual(getResp.FilePermissionKey, "") + + fileAttributes2, err := file.ParseNTFSFileAttributes(getResp.FileAttributes) + _require.NoError(err) + _require.NotNil(fileAttributes2) + _require.True(fileAttributes2.Hidden) + _require.True(fileAttributes2.ReadOnly) + _require.EqualValues(fileAttributes, fileAttributes2) + _require.NotNil(getResp.ETag) + _require.NotNil(getResp.RequestID) + _require.NotNil(getResp.Version) + _require.Equal(getResp.Date.IsZero(), false) + _require.NotNil(getResp.IsServerEncrypted) +} + +func (f *FileRecordedTestsSuite) TestFileSetMetadataUsingOAuth() { + _require := require.New(f.T()) + testName := f.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := testcommon.GetGenericTokenCredential() + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fileName := testcommon.GenerateFileName(testName) + fileURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + fileName + + options := &file.ClientOptions{FileRequestIntent: to.Ptr(file.ShareTokenIntentBackup)} + testcommon.SetClientOptions(f.T(), &options.ClientOptions) + fileClient, err := file.NewClient(fileURL, cred, options) + _require.NoError(err) + + _, err = fileClient.Create(context.Background(), 2048, nil) + _require.NoError(err) + + metadata := map[string]*string{ + "Foo": to.Ptr("Foovalue"), + "Bar": to.Ptr("Barvalue"), + } + _, err = fileClient.SetMetadata(context.Background(), &file.SetMetadataOptions{ + Metadata: metadata, + }) + _require.NoError(err) + + getResp, err := fileClient.GetProperties(context.Background(), nil) + _require.NoError(err) + _require.Equal(*getResp.ContentLength, int64(2048)) + _require.EqualValues(getResp.Metadata, metadata) +} + +func (f *FileRecordedTestsSuite) TestFileUploadClearListRangeUsingOAuth() { + _require := require.New(f.T()) + testName := f.T().Name() + + accountName, _ := testcommon.GetGenericAccountInfo(testcommon.TestAccountDefault) + _require.Greater(len(accountName), 0) + + cred, err := testcommon.GetGenericTokenCredential() + _require.NoError(err) + + svcClient, err := testcommon.GetServiceClient(f.T(), testcommon.TestAccountDefault, nil) + _require.NoError(err) + + shareName := testcommon.GenerateShareName(testName) + shareClient := testcommon.CreateNewShare(context.Background(), _require, shareName, svcClient) + defer testcommon.DeleteShare(context.Background(), _require, shareClient) + + fileName := testcommon.GenerateFileName(testName) + fileURL := "https://" + accountName + ".file.core.windows.net/" + shareName + "/" + fileName + + options := &file.ClientOptions{FileRequestIntent: to.Ptr(file.ShareTokenIntentBackup)} + testcommon.SetClientOptions(f.T(), &options.ClientOptions) + fileClient, err := file.NewClient(fileURL, cred, options) + _require.NoError(err) + + _, err = fileClient.Create(context.Background(), 2048, nil) + _require.NoError(err) + + contentSize := 1024 * 2 // 2KB + contentR, contentD := testcommon.GenerateData(contentSize) + md5Value := md5.Sum(contentD) + contentMD5 := md5Value[:] + + uResp, err := fileClient.UploadRange(context.Background(), 0, contentR, &file.UploadRangeOptions{ + TransactionalValidation: file.TransferValidationTypeMD5(contentMD5), + }) + _require.NoError(err) + _require.NotNil(uResp.ContentMD5) + _require.EqualValues(uResp.ContentMD5, contentMD5) + + rangeList, err := fileClient.GetRangeList(context.Background(), nil) + _require.NoError(err) + _require.Len(rangeList.Ranges, 1) + _require.EqualValues(*rangeList.Ranges[0], file.ShareFileRange{Start: to.Ptr(int64(0)), End: to.Ptr(int64(contentSize - 1))}) + + cResp, err := fileClient.ClearRange(context.Background(), file.HTTPRange{Offset: 0, Count: int64(contentSize)}, nil) + _require.NoError(err) + _require.Nil(cResp.ContentMD5) + + rangeList2, err := fileClient.GetRangeList(context.Background(), nil) + _require.NoError(err) + _require.Len(rangeList2.Ranges, 0) +} + // TODO: Add tests for retry header options From 50415b8e9a7f7f701e90af5f3af4ff33b48332f0 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 30 Jun 2023 11:26:35 +0530 Subject: [PATCH 07/10] Adding changelog --- sdk/storage/azfile/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/storage/azfile/CHANGELOG.md b/sdk/storage/azfile/CHANGELOG.md index cab1236b22d6..b80694ee95c6 100644 --- a/sdk/storage/azfile/CHANGELOG.md +++ b/sdk/storage/azfile/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features Added * Updated service version to STG 87(2022-11-02). +* Added OAuth support. ### Breaking Changes From 96cf7245ced265a5d4ec1dd4e455f0ad83b3bb51 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 30 Jun 2023 12:14:31 +0530 Subject: [PATCH 08/10] Updating recordings --- sdk/storage/azfile/assets.json | 2 +- sdk/storage/azfile/service/client_test.go | 2 ++ sdk/storage/azfile/share/client_test.go | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/storage/azfile/assets.json b/sdk/storage/azfile/assets.json index e15822bae44a..b132a321ed62 100644 --- a/sdk/storage/azfile/assets.json +++ b/sdk/storage/azfile/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "go", "TagPrefix": "go/storage/azfile", - "Tag": "go/storage/azfile_e512da82e9" + "Tag": "go/storage/azfile_b53cd6a3cc" } diff --git a/sdk/storage/azfile/service/client_test.go b/sdk/storage/azfile/service/client_test.go index 0b1e6240e12b..8f0b3982be48 100644 --- a/sdk/storage/azfile/service/client_test.go +++ b/sdk/storage/azfile/service/client_test.go @@ -463,6 +463,7 @@ func (s *ServiceRecordedTestsSuite) TestServiceOAuthNegative() { _require.NoError(err) options := &service.ClientOptions{FileRequestIntent: to.Ptr(service.ShareTokenIntentBackup)} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) svcClient, err := service.NewClient("https://"+accountName+".file.core.windows.net/", cred, options) _require.NoError(err) @@ -500,6 +501,7 @@ func (s *ServiceRecordedTestsSuite) TestServiceCreateDeleteDirOAuth() { // create service client using token credential options := &service.ClientOptions{FileRequestIntent: to.Ptr(service.ShareTokenIntentBackup)} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) svcClientOAuth, err := service.NewClient("https://"+accountName+".file.core.windows.net/", cred, options) _require.NoError(err) diff --git a/sdk/storage/azfile/share/client_test.go b/sdk/storage/azfile/share/client_test.go index 18cf3fd6ba2a..9e1cc6976bb8 100644 --- a/sdk/storage/azfile/share/client_test.go +++ b/sdk/storage/azfile/share/client_test.go @@ -1469,8 +1469,9 @@ func (s *ShareRecordedTestsSuite) TestShareOAuthNegative() { cred, err := testcommon.GetGenericTokenCredential() _require.NoError(err) - options := &share.ClientOptions{FileRequestIntent: to.Ptr(share.TokenIntentBackup)} shareName := testcommon.GenerateShareName(testName) + options := &share.ClientOptions{FileRequestIntent: to.Ptr(share.TokenIntentBackup)} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) shareClient, err := share.NewClient("https://"+accountName+".file.core.windows.net/"+shareName, cred, options) _require.NoError(err) @@ -1509,6 +1510,7 @@ func (s *ShareRecordedTestsSuite) TestShareCreateAndGetPermissionOAuth() { defer testcommon.DeleteShare(context.Background(), _require, shareClient) options := &share.ClientOptions{FileRequestIntent: to.Ptr(share.TokenIntentBackup)} + testcommon.SetClientOptions(s.T(), &options.ClientOptions) shareClientOAuth, err := share.NewClient("https://"+accountName+".file.core.windows.net/"+shareName, cred, options) _require.NoError(err) From eaeb789927715b833a3d90374557f4db314bf51e Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 30 Jun 2023 16:44:39 +0530 Subject: [PATCH 09/10] Format checks --- sdk/storage/azfile/directory/client.go | 1 + sdk/storage/azfile/file/client.go | 1 + sdk/storage/azfile/service/client.go | 1 + sdk/storage/azfile/share/client.go | 1 + 4 files changed, 4 insertions(+) diff --git a/sdk/storage/azfile/directory/client.go b/sdk/storage/azfile/directory/client.go index 24da6b41128a..c72910cb23cc 100644 --- a/sdk/storage/azfile/directory/client.go +++ b/sdk/storage/azfile/directory/client.go @@ -32,6 +32,7 @@ type Client base.Client[generated.DirectoryClient] // - directoryURL - the URL of the directory e.g. https://.file.core.windows.net/share/directory // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// // Note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(directoryURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) diff --git a/sdk/storage/azfile/file/client.go b/sdk/storage/azfile/file/client.go index ccc21fb6f842..bed80db6f1d2 100644 --- a/sdk/storage/azfile/file/client.go +++ b/sdk/storage/azfile/file/client.go @@ -38,6 +38,7 @@ type Client base.Client[generated.FileClient] // - fileURL - the URL of the file e.g. https://.file.core.windows.net/share/directoryPath/file // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// // Note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(fileURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { authPolicy := runtime.NewBearerTokenPolicy(cred, []string{shared.TokenScope}, nil) diff --git a/sdk/storage/azfile/service/client.go b/sdk/storage/azfile/service/client.go index 64de91aeebc0..2811623b7673 100644 --- a/sdk/storage/azfile/service/client.go +++ b/sdk/storage/azfile/service/client.go @@ -34,6 +34,7 @@ type Client base.Client[generated.ServiceClient] // - serviceURL - the URL of the storage account e.g. https://.file.core.windows.net/ // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// // Note that service-level operations do not support token credential authentication. // This constructor exists to allow the construction of a share.Client that has token credential authentication. // Also note that ClientOptions.FileRequestIntent is currently required for token authentication. diff --git a/sdk/storage/azfile/share/client.go b/sdk/storage/azfile/share/client.go index ac2c9d36bc3d..8810141fcce0 100644 --- a/sdk/storage/azfile/share/client.go +++ b/sdk/storage/azfile/share/client.go @@ -34,6 +34,7 @@ type Client base.Client[generated.ShareClient] // - shareURL - the URL of the share e.g. https://.file.core.windows.net/share // - cred - an Azure AD credential, typically obtained via the azidentity module // - options - client options; pass nil to accept the default values +// // Note that the only share-level operations that support token credential authentication are CreatePermission and GetPermission. // Also note that ClientOptions.FileRequestIntent is currently required for token authentication. func NewClient(shareURL string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) { From bc720478561cc32306f18760160084b88a1e6e83 Mon Sep 17 00:00:00 2001 From: Sourav Gupta Date: Fri, 30 Jun 2023 18:17:45 +0530 Subject: [PATCH 10/10] Adding Storage File Data Privileged Contributor role to the SPN --- sdk/storage/azfile/test-resources.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdk/storage/azfile/test-resources.json b/sdk/storage/azfile/test-resources.json index c6259f7ab02f..912321ecd276 100644 --- a/sdk/storage/azfile/test-resources.json +++ b/sdk/storage/azfile/test-resources.json @@ -25,6 +25,7 @@ "blobDataContributorRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", "contributorRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c')]", "blobDataOwnerRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "fileElevatedContributorCustomRoleId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/9276e164-dbbe-4f3f-b4db-cfdd87eebbe9')]", "primaryAccountName": "[concat(parameters('baseName'), 'prim')]", "immutableAccountName": "[concat(parameters('baseName'), 'imm')]", "primaryEncryptionScopeName": "encryptionScope", @@ -85,6 +86,15 @@ "principalId": "[parameters('testApplicationOid')]" } }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "[variables('authorizationApiVersion')]", + "name": "[guid(concat('fileElevatedContributorCustomRoleId', resourceGroup().id))]", + "properties": { + "roleDefinitionId": "[variables('fileElevatedContributorCustomRoleId')]", + "principalId": "[parameters('testApplicationOid')]" + } + }, { "type": "Microsoft.Storage/storageAccounts", "apiVersion": "[variables('mgmtApiVersion')]",