Storage/STG101/Cross-tenant principal bound sas (#6863)
* Cross-tenant principal bound sas * Fix test failure
This commit is contained in:
parent
b0108b0d13
commit
464b57b2a0
@ -275,6 +275,11 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
* will be truncated to second.
|
||||
*/
|
||||
Azure::DateTime StartsOn = std::chrono::system_clock::now();
|
||||
|
||||
/**
|
||||
* The delegated user tenant id in Azure AD.
|
||||
*/
|
||||
Nullable<std::string> DelegatedUserTid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/storage/common/crypt.hpp>
|
||||
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, sduoid */
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, skdutid, sduoid */
|
||||
|
||||
namespace Azure { namespace Storage { namespace Sas {
|
||||
|
||||
@ -261,10 +261,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
|
||||
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion
|
||||
+ "\n\n\n\n\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "")
|
||||
+ "\n" + protocol + "\n" + SasVersion + "\n" + resource + "\n" + snapshotVersion + "\n"
|
||||
+ EncryptionScope + "\n" + CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding
|
||||
+ "\n" + ContentLanguage + "\n" + ContentType;
|
||||
+ "\n\n\n\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + resource + "\n" + snapshotVersion + "\n"
|
||||
+ EncryptionScope + "\n\n\n" + CacheControl + "\n" + ContentDisposition + "\n"
|
||||
+ ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
|
||||
std::string signature = Azure::Core::Convert::Base64Encode(_internal::HmacSha256(
|
||||
std::vector<uint8_t>(stringToSign.begin(), stringToSign.end()),
|
||||
@ -294,6 +298,12 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
"sks", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedService));
|
||||
builder.AppendQueryParameter(
|
||||
"skv", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedVersion));
|
||||
if (userDelegationKey.SignedDelegatedUserTid.HasValue())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
"skdutid",
|
||||
_internal::UrlEncodeQueryParameter(userDelegationKey.SignedDelegatedUserTid.Value()));
|
||||
}
|
||||
if (!DelegatedUserObjectId.empty())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
@ -402,10 +412,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
return Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n" + canonicalName + "\n"
|
||||
+ userDelegationKey.SignedObjectId + "\n" + userDelegationKey.SignedTenantId + "\n"
|
||||
+ signedStartsOnStr + "\n" + signedExpiresOnStr + "\n" + userDelegationKey.SignedService
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n\n\n\n\n" + DelegatedUserObjectId + "\n"
|
||||
+ (IPRange.HasValue() ? IPRange.Value() : "") + "\n" + protocol + "\n" + SasVersion + "\n"
|
||||
+ resource + "\n" + snapshotVersion + "\n" + EncryptionScope + "\n" + CacheControl + "\n"
|
||||
+ ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n\n\n\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + resource + "\n" + snapshotVersion + "\n"
|
||||
+ EncryptionScope + "\n\n\n" + CacheControl + "\n" + ContentDisposition + "\n"
|
||||
+ ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Sas
|
||||
|
||||
@ -184,6 +184,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.Expiry = expiresOn.ToString(
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.DelegatedUserTid = options.DelegatedUserTid;
|
||||
return _detail::ServiceClient::GetUserDelegationKey(
|
||||
*m_pipeline, m_serviceUrl, protocolLayerOptions, _internal::WithReplicaStatus(context));
|
||||
}
|
||||
|
||||
@ -880,7 +880,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
return {};
|
||||
}
|
||||
|
||||
TEST_F(BlobSasTest, DISABLED_PrincipalBoundDelegationSas)
|
||||
TEST_F(BlobSasTest, PrincipalBoundDelegationSas_LIVEONLY_)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
@ -930,4 +930,67 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
InitStorageClientOptions<Blobs::BlobClientOptions>());
|
||||
EXPECT_THROW(blobClient2.GetProperties(), StorageException);
|
||||
}
|
||||
|
||||
TEST_F(BlobSasTest, DISABLED_PrincipalBoundDelegationSas_CrossTenant)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
|
||||
auto keyCredential
|
||||
= _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
|
||||
auto accountName = keyCredential->AccountName;
|
||||
Azure::Identity::ClientSecretCredentialOptions credentialOptions;
|
||||
credentialOptions.AdditionallyAllowedTenants = {"*"};
|
||||
auto endUserCredential = std::make_shared<Azure::Identity::ClientSecretCredential>(
|
||||
GetEnv("AZURE_TENANT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_SECRET_CROSS_TENANT"));
|
||||
auto delegatedUserObjectId = getObjectIdFromTokenCredential(endUserCredential);
|
||||
|
||||
auto blobServiceClient = Blobs::BlobServiceClient(
|
||||
m_blobServiceClient->GetUrl(),
|
||||
GetTestCredential(),
|
||||
InitStorageClientOptions<Blobs::BlobClientOptions>());
|
||||
Blobs::Models::UserDelegationKey userDelegationKey;
|
||||
{
|
||||
Blobs::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "4ab3a968-f1ae-47a6-b82c-f654612122a9";
|
||||
userDelegationKey = blobServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
|
||||
auto blobContainerClient = *m_blobContainerClient;
|
||||
auto blobClient = *m_blockBlobClient;
|
||||
const std::string blobName = m_blobName;
|
||||
|
||||
Sas::BlobSasBuilder blobSasBuilder;
|
||||
blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
blobSasBuilder.StartsOn = sasStartsOn;
|
||||
blobSasBuilder.ExpiresOn = sasExpiresOn;
|
||||
blobSasBuilder.BlobContainerName = m_containerName;
|
||||
blobSasBuilder.BlobName = blobName;
|
||||
blobSasBuilder.Resource = Sas::BlobSasResource::Blob;
|
||||
blobSasBuilder.DelegatedUserObjectId = delegatedUserObjectId;
|
||||
|
||||
blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All);
|
||||
auto sasToken = blobSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
|
||||
Blobs::BlockBlobClient blobClient1(
|
||||
AppendQueryParameters(Azure::Core::Url(blobClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
InitStorageClientOptions<Blobs::BlobClientOptions>());
|
||||
EXPECT_NO_THROW(blobClient1.Download());
|
||||
|
||||
{
|
||||
Blobs::GetUserDelegationKeyOptions options;
|
||||
// Invalid Tenant Id
|
||||
options.DelegatedUserTid = "00000000-0000-0000-0000-000000000000";
|
||||
userDelegationKey = blobServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
sasToken = blobSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
Blobs::BlockBlobClient blobClient2(
|
||||
AppendQueryParameters(Azure::Core::Url(blobClient.GetUrl()), sasToken),
|
||||
GetTestCredential(),
|
||||
InitStorageClientOptions<Blobs::BlobClientOptions>());
|
||||
EXPECT_THROW(blobClient2.Download(), StorageException);
|
||||
}
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -321,7 +321,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
EXPECT_EQ(
|
||||
downloadedProperties.DefaultServiceVersion.HasValue(),
|
||||
properties.DefaultServiceVersion.HasValue());
|
||||
if (downloadedProperties.DefaultServiceVersion.HasValue())
|
||||
if (downloadedProperties.DefaultServiceVersion.HasValue() && !m_testContext.IsPlaybackMode())
|
||||
{
|
||||
EXPECT_EQ(
|
||||
downloadedProperties.DefaultServiceVersion.Value(),
|
||||
|
||||
@ -27,7 +27,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
/**
|
||||
* The version used for the operations to Azure storage services.
|
||||
*/
|
||||
constexpr static const char* ApiVersion = "2026-02-06";
|
||||
constexpr static const char* ApiVersion = "2026-04-06";
|
||||
} // namespace _detail
|
||||
namespace Models {
|
||||
namespace _detail {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/storage/common/crypt.hpp>
|
||||
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, saoid, suoid, scid, sduoid */
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, saoid, suoid, scid, skdutid, sduoid */
|
||||
|
||||
namespace Azure { namespace Storage { namespace Sas {
|
||||
namespace {
|
||||
@ -226,9 +226,12 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
|
||||
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n"
|
||||
+ PreauthorizedAgentObjectId + "\n" + AgentObjectId + "\n" + CorrelationId + "\n" + "\n"
|
||||
+ DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + resource + "\n" + "\n" + EncryptionScope + "\n"
|
||||
+ PreauthorizedAgentObjectId + "\n" + AgentObjectId + "\n" + CorrelationId + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + resource + "\n" + "\n" + EncryptionScope + "\n\n\n"
|
||||
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
|
||||
+ "\n" + ContentType;
|
||||
|
||||
@ -273,6 +276,12 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
{
|
||||
builder.AppendQueryParameter("scid", _internal::UrlEncodeQueryParameter(CorrelationId));
|
||||
}
|
||||
if (userDelegationKey.SignedDelegatedUserTid.HasValue())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
"skdutid",
|
||||
_internal::UrlEncodeQueryParameter(userDelegationKey.SignedDelegatedUserTid.Value()));
|
||||
}
|
||||
if (!DelegatedUserObjectId.empty())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
@ -365,10 +374,14 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
+ userDelegationKey.SignedObjectId + "\n" + userDelegationKey.SignedTenantId + "\n"
|
||||
+ signedStartsOnStr + "\n" + signedExpiresOnStr + "\n" + userDelegationKey.SignedService
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n" + PreauthorizedAgentObjectId + "\n"
|
||||
+ AgentObjectId + "\n" + CorrelationId + "\n\n" + DelegatedUserObjectId + "\n"
|
||||
+ (IPRange.HasValue() ? IPRange.Value() : "") + "\n" + protocol + "\n" + SasVersion + "\n"
|
||||
+ resource + "\n" + "\n" + EncryptionScope + "\n" + CacheControl + "\n" + ContentDisposition
|
||||
+ "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
+ AgentObjectId + "\n" + CorrelationId + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + resource + "\n" + "\n" + EncryptionScope + "\n\n\n"
|
||||
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
|
||||
+ "\n" + ContentType;
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Sas
|
||||
|
||||
@ -61,7 +61,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value()));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
if (options.ContinuationToken.HasValue() && !options.ContinuationToken.Value().empty())
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter(
|
||||
@ -162,7 +162,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value()));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
if (options.Resource.HasValue() && !options.Resource.Value().ToString().empty())
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter(
|
||||
@ -350,7 +350,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value()));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
if (options.Recursive.HasValue())
|
||||
{
|
||||
request.GetUrl().AppendQueryParameter(
|
||||
@ -448,7 +448,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
"If-Unmodified-Since",
|
||||
options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
auto pRawResponse = pipeline.Send(request, context);
|
||||
auto httpStatusCode = pRawResponse->GetStatusCode();
|
||||
if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
|
||||
@ -495,7 +495,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.SetHeader("x-ms-acl", options.Acl.Value());
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
auto pRawResponse = pipeline.Send(request, context);
|
||||
auto httpStatusCode = pRawResponse->GetStatusCode();
|
||||
if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
|
||||
@ -548,7 +548,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.SetHeader("x-ms-undelete-source", options.UndeleteSource.Value());
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
auto pRawResponse = pipeline.Send(request, context);
|
||||
auto httpStatusCode = pRawResponse->GetStatusCode();
|
||||
if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
|
||||
@ -599,7 +599,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
"If-Unmodified-Since",
|
||||
options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
auto pRawResponse = pipeline.Send(request, context);
|
||||
auto httpStatusCode = pRawResponse->GetStatusCode();
|
||||
if (httpStatusCode != Core::Http::HttpStatusCode::Ok)
|
||||
@ -698,7 +698,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
"If-Unmodified-Since",
|
||||
options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123));
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
if (options.EncryptionKey.HasValue() && !options.EncryptionKey.Value().empty())
|
||||
{
|
||||
request.SetHeader("x-ms-encryption-key", options.EncryptionKey.Value());
|
||||
@ -782,7 +782,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
|
||||
{
|
||||
request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value());
|
||||
}
|
||||
request.SetHeader("x-ms-version", "2026-02-06");
|
||||
request.SetHeader("x-ms-version", "2026-04-06");
|
||||
if (options.EncryptionKey.HasValue() && !options.EncryptionKey.Value().empty())
|
||||
{
|
||||
request.SetHeader("x-ms-encryption-key", options.EncryptionKey.Value());
|
||||
|
||||
@ -88,12 +88,12 @@ directive:
|
||||
"name": "ApiVersion",
|
||||
"modelAsString": false
|
||||
},
|
||||
"enum": ["2026-02-06"]
|
||||
"enum": ["2026-04-06"]
|
||||
};
|
||||
- from: swagger-document
|
||||
where: $.parameters
|
||||
transform: >
|
||||
$.ApiVersionParameter.enum[0] = "2026-02-06";
|
||||
$.ApiVersionParameter.enum[0] = "2026-04-06";
|
||||
```
|
||||
|
||||
### Rename Operations
|
||||
|
||||
@ -886,7 +886,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
return {};
|
||||
}
|
||||
|
||||
TEST_F(DataLakeSasTest, DISABLED_PrincipalBoundDelegationSas)
|
||||
TEST_F(DataLakeSasTest, PrincipalBoundDelegationSas_LIVEONLY_)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
@ -932,4 +932,61 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>());
|
||||
EXPECT_THROW(fileClient2.GetProperties(), StorageException);
|
||||
}
|
||||
|
||||
TEST_F(DataLakeSasTest, DISABLED_PrincipalBoundDelegationSas_CrossTenant)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
|
||||
auto keyCredential = _internal::ParseConnectionString(AdlsGen2ConnectionString()).KeyCredential;
|
||||
auto accountName = keyCredential->AccountName;
|
||||
|
||||
Azure::Identity::ClientSecretCredentialOptions credentialOptions;
|
||||
credentialOptions.AdditionallyAllowedTenants = {"*"};
|
||||
auto endUserCredential = std::make_shared<Azure::Identity::ClientSecretCredential>(
|
||||
GetEnv("AZURE_TENANT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_SECRET_CROSS_TENANT"));
|
||||
auto delegatedUserObjectId = getObjectIdFromTokenCredential(endUserCredential);
|
||||
|
||||
Files::DataLake::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "4ab3a968-f1ae-47a6-b82c-f654612122a9";
|
||||
Files::DataLake::Models::UserDelegationKey userDelegationKey
|
||||
= GetDataLakeServiceClientOAuth().GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
|
||||
std::string fileName = RandomString();
|
||||
|
||||
auto dataLakeFileSystemClient = *m_fileSystemClient;
|
||||
auto dataLakeFileClient = dataLakeFileSystemClient.GetFileClient(fileName);
|
||||
dataLakeFileClient.Create();
|
||||
|
||||
Sas::DataLakeSasBuilder fileSasBuilder;
|
||||
fileSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
fileSasBuilder.StartsOn = sasStartsOn;
|
||||
fileSasBuilder.ExpiresOn = sasExpiresOn;
|
||||
fileSasBuilder.FileSystemName = m_fileSystemName;
|
||||
fileSasBuilder.Path = fileName;
|
||||
fileSasBuilder.Resource = Sas::DataLakeSasResource::File;
|
||||
fileSasBuilder.DelegatedUserObjectId = delegatedUserObjectId;
|
||||
|
||||
fileSasBuilder.SetPermissions(Sas::DataLakeSasPermissions::All);
|
||||
auto sasToken = fileSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
|
||||
Files::DataLake::DataLakeFileClient fileClient1(
|
||||
AppendQueryParameters(Azure::Core::Url(dataLakeFileClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>());
|
||||
EXPECT_NO_THROW(fileClient1.GetProperties());
|
||||
|
||||
options.DelegatedUserTid = "00000000-0000-0000-0000-000000000000";
|
||||
userDelegationKey
|
||||
= GetDataLakeServiceClientOAuth().GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
|
||||
sasToken = fileSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
Files::DataLake::DataLakeFileClient fileClient2(
|
||||
AppendQueryParameters(Azure::Core::Url(dataLakeFileClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>());
|
||||
EXPECT_THROW(fileClient2.GetProperties(), StorageException);
|
||||
}
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -377,6 +377,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
* will be truncated to second.
|
||||
*/
|
||||
Azure::DateTime StartsOn = std::chrono::system_clock::now();
|
||||
|
||||
/**
|
||||
* The delegated user tenant id in Azure AD.
|
||||
*/
|
||||
Nullable<std::string> DelegatedUserTid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/storage/common/crypt.hpp>
|
||||
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, sduoid */
|
||||
/* cSpell:ignore rscc, rscd, rsce, rscl, rsct, skoid, sktid, skdutid, sduoid */
|
||||
|
||||
namespace Azure { namespace Storage { namespace Sas {
|
||||
|
||||
@ -185,8 +185,11 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
|
||||
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n\n"
|
||||
+ DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + CacheControl + "\n" + ContentDisposition + "\n"
|
||||
+ ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
|
||||
@ -224,6 +227,12 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
"sks", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedService));
|
||||
builder.AppendQueryParameter(
|
||||
"skv", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedVersion));
|
||||
if (userDelegationKey.SignedDelegatedUserTid.HasValue())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
"skdutid",
|
||||
_internal::UrlEncodeQueryParameter(userDelegationKey.SignedDelegatedUserTid.Value()));
|
||||
}
|
||||
if (!DelegatedUserObjectId.empty())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
@ -307,10 +316,13 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
return Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n" + canonicalName + "\n"
|
||||
+ userDelegationKey.SignedObjectId + "\n" + userDelegationKey.SignedTenantId + "\n"
|
||||
+ signedStartsOnStr + "\n" + signedExpiresOnStr + "\n" + userDelegationKey.SignedService
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n\n" + DelegatedUserObjectId + "\n"
|
||||
+ (IPRange.HasValue() ? IPRange.Value() : "") + "\n" + protocol + "\n" + SasVersion + "\n"
|
||||
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
|
||||
+ "\n" + ContentType;
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion + "\n" + CacheControl + "\n" + ContentDisposition + "\n"
|
||||
+ ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Sas
|
||||
|
||||
@ -194,6 +194,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.Expiry = expiresOn.ToString(
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.DelegatedUserTid = options.DelegatedUserTid;
|
||||
return _detail::ServiceClient::GetUserDelegationKey(
|
||||
*m_pipeline, m_serviceUrl, protocolLayerOptions, context);
|
||||
}
|
||||
|
||||
@ -753,7 +753,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
return {};
|
||||
}
|
||||
|
||||
TEST_F(ShareSasTest, DISABLED_PrincipalBoundDelegationSas)
|
||||
TEST_F(ShareSasTest, PrincipalBoundDelegationSas_LIVEONLY_)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
@ -791,10 +791,12 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
fileSasBuilder.SetPermissions(Sas::ShareFileSasPermissions::All);
|
||||
auto sasToken = fileSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
|
||||
auto fileOptions = InitStorageClientOptions<Files::Shares::ShareClientOptions>();
|
||||
fileOptions.ShareTokenIntent = Files::Shares::Models::ShareTokenIntent::Backup;
|
||||
Files::Shares::ShareFileClient fileClient1(
|
||||
AppendQueryParameters(Azure::Core::Url(fileClient.GetUrl()), sasToken),
|
||||
GetTestCredential(),
|
||||
InitStorageClientOptions<Files::Shares::ShareClientOptions>());
|
||||
fileOptions);
|
||||
EXPECT_NO_THROW(fileClient1.GetProperties());
|
||||
|
||||
fileSasBuilder.DelegatedUserObjectId = "invalidObjectId";
|
||||
@ -802,7 +804,73 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
Files::Shares::ShareFileClient fileClient2(
|
||||
AppendQueryParameters(Azure::Core::Url(fileClient.GetUrl()), sasToken),
|
||||
GetTestCredential(),
|
||||
fileOptions);
|
||||
EXPECT_THROW(fileClient2.GetProperties(), StorageException);
|
||||
}
|
||||
|
||||
TEST_F(ShareSasTest, DISABLED_PrincipalBoundDelegationSas_CrossTenant)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
|
||||
std::string fileName = RandomString();
|
||||
|
||||
auto keyCredential
|
||||
= _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
|
||||
auto accountName = keyCredential->AccountName;
|
||||
Azure::Identity::ClientSecretCredentialOptions credentialOptions;
|
||||
credentialOptions.AdditionallyAllowedTenants = {"*"};
|
||||
auto endUserCredential = std::make_shared<Azure::Identity::ClientSecretCredential>(
|
||||
GetEnv("AZURE_TENANT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_SECRET_CROSS_TENANT"));
|
||||
auto delegatedUserObjectId = getObjectIdFromTokenCredential(endUserCredential);
|
||||
|
||||
auto shareServiceClient = Files::Shares::ShareServiceClient(
|
||||
m_shareServiceClient->GetUrl(),
|
||||
GetTestCredential(),
|
||||
InitStorageClientOptions<Files::Shares::ShareClientOptions>());
|
||||
Files::Shares::Models::UserDelegationKey userDelegationKey;
|
||||
{
|
||||
Files::Shares::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "4ab3a968-f1ae-47a6-b82c-f654612122a9";
|
||||
userDelegationKey = shareServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
|
||||
Sas::ShareSasBuilder fileSasBuilder;
|
||||
fileSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
fileSasBuilder.StartsOn = sasStartsOn;
|
||||
fileSasBuilder.ExpiresOn = sasExpiresOn;
|
||||
fileSasBuilder.ShareName = m_shareName;
|
||||
fileSasBuilder.FilePath = fileName;
|
||||
fileSasBuilder.Resource = Sas::ShareSasResource::File;
|
||||
fileSasBuilder.DelegatedUserObjectId = delegatedUserObjectId;
|
||||
|
||||
auto shareClient = *m_shareClient;
|
||||
auto fileClient = shareClient.GetRootDirectoryClient().GetFileClient(fileName);
|
||||
fileClient.Create(1);
|
||||
|
||||
auto fileOptions = InitStorageClientOptions<Files::Shares::ShareClientOptions>();
|
||||
fileOptions.ShareTokenIntent = Files::Shares::Models::ShareTokenIntent::Backup;
|
||||
fileSasBuilder.SetPermissions(Sas::ShareFileSasPermissions::All);
|
||||
auto sasToken = fileSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
|
||||
Files::Shares::ShareFileClient fileClient1(
|
||||
AppendQueryParameters(Azure::Core::Url(fileClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
fileOptions);
|
||||
EXPECT_NO_THROW(fileClient1.GetProperties());
|
||||
|
||||
{
|
||||
Files::Shares::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "00000000-0000-0000-0000-000000000000";
|
||||
userDelegationKey = shareServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
sasToken = fileSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
Files::Shares::ShareFileClient fileClient2(
|
||||
AppendQueryParameters(Azure::Core::Url(fileClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
fileOptions);
|
||||
EXPECT_THROW(fileClient2.GetProperties(), StorageException);
|
||||
}
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -208,6 +208,11 @@ namespace Azure { namespace Storage { namespace Queues {
|
||||
* will be truncated to second.
|
||||
*/
|
||||
Azure::DateTime StartsOn = std::chrono::system_clock::now();
|
||||
|
||||
/**
|
||||
* The delegated user tenant id in Azure AD.
|
||||
*/
|
||||
Nullable<std::string> DelegatedUserTid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/storage/common/crypt.hpp>
|
||||
|
||||
/* cSpell:ignore skoid, sktid, sduoid */
|
||||
/* cSpell:ignore skoid, sktid, skdutid, sduoid */
|
||||
|
||||
namespace Azure { namespace Storage { namespace Sas {
|
||||
|
||||
@ -113,8 +113,11 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
|
||||
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
|
||||
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n\n"
|
||||
+ DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion;
|
||||
|
||||
std::string signature = Azure::Core::Convert::Base64Encode(_internal::HmacSha256(
|
||||
@ -150,6 +153,12 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
"sks", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedService));
|
||||
builder.AppendQueryParameter(
|
||||
"skv", _internal::UrlEncodeQueryParameter(userDelegationKey.SignedVersion));
|
||||
if (userDelegationKey.SignedDelegatedUserTid.HasValue())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
"skdutid",
|
||||
_internal::UrlEncodeQueryParameter(userDelegationKey.SignedDelegatedUserTid.Value()));
|
||||
}
|
||||
if (!DelegatedUserObjectId.empty())
|
||||
{
|
||||
builder.AppendQueryParameter(
|
||||
@ -204,7 +213,11 @@ namespace Azure { namespace Storage { namespace Sas {
|
||||
return Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n" + canonicalName + "\n"
|
||||
+ userDelegationKey.SignedObjectId + "\n" + userDelegationKey.SignedTenantId + "\n"
|
||||
+ signedStartsOnStr + "\n" + signedExpiresOnStr + "\n" + userDelegationKey.SignedService
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n\n" + DelegatedUserObjectId + "\n"
|
||||
+ (IPRange.HasValue() ? IPRange.Value() : "") + "\n" + protocol + "\n" + SasVersion;
|
||||
+ "\n" + userDelegationKey.SignedVersion + "\n"
|
||||
+ (userDelegationKey.SignedDelegatedUserTid.HasValue()
|
||||
? userDelegationKey.SignedDelegatedUserTid.Value()
|
||||
: "")
|
||||
+ "\n" + DelegatedUserObjectId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n"
|
||||
+ protocol + "\n" + SasVersion;
|
||||
}
|
||||
}}} // namespace Azure::Storage::Sas
|
||||
|
||||
@ -203,6 +203,7 @@ namespace Azure { namespace Storage { namespace Queues {
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.Expiry = expiresOn.ToString(
|
||||
Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate);
|
||||
protocolLayerOptions.KeyInfo.DelegatedUserTid = options.DelegatedUserTid;
|
||||
return _detail::ServiceClient::GetUserDelegationKey(
|
||||
*m_pipeline, m_serviceUrl, protocolLayerOptions, context);
|
||||
}
|
||||
|
||||
@ -499,7 +499,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
return {};
|
||||
}
|
||||
|
||||
TEST_F(QueueSasTest, DISABLED_PrincipalBoundDelegationSas)
|
||||
TEST_F(QueueSasTest, PrincipalBoundDelegationSas_LIVEONLY_)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
@ -546,4 +546,61 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
EXPECT_THROW(queueClient2.GetProperties(), StorageException);
|
||||
}
|
||||
|
||||
TEST_F(QueueSasTest, DISABLED_PrincipalBoundDelegationSas_CrossTenant)
|
||||
{
|
||||
auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60);
|
||||
|
||||
auto keyCredential
|
||||
= _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
|
||||
Azure::Identity::ClientSecretCredentialOptions credentialOptions;
|
||||
credentialOptions.AdditionallyAllowedTenants = {"*"};
|
||||
auto endUserCredential = std::make_shared<Azure::Identity::ClientSecretCredential>(
|
||||
GetEnv("AZURE_TENANT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_ID_CROSS_TENANT"),
|
||||
GetEnv("AZURE_CLIENT_SECRET_CROSS_TENANT"));
|
||||
auto delegatedUserObjectId = getObjectIdFromTokenCredential(endUserCredential);
|
||||
|
||||
auto queueServiceClient = Queues::QueueServiceClient(
|
||||
m_queueServiceClient->GetUrl(),
|
||||
GetTestCredential(),
|
||||
InitStorageClientOptions<Queues::QueueClientOptions>());
|
||||
Queues::Models::UserDelegationKey userDelegationKey;
|
||||
{
|
||||
Queues::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "4ab3a968-f1ae-47a6-b82c-f654612122a9";
|
||||
userDelegationKey = queueServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
|
||||
Sas::QueueSasBuilder queueSasBuilder;
|
||||
queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
queueSasBuilder.StartsOn = sasStartsOn;
|
||||
queueSasBuilder.ExpiresOn = sasExpiresOn;
|
||||
queueSasBuilder.QueueName = m_queueName;
|
||||
queueSasBuilder.DelegatedUserObjectId = delegatedUserObjectId;
|
||||
|
||||
auto queueClient = *m_queueClient;
|
||||
auto accountName = keyCredential->AccountName;
|
||||
|
||||
queueSasBuilder.SetPermissions(Sas::QueueSasPermissions::All);
|
||||
auto sasToken = queueSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
|
||||
Queues::QueueClient queueClient1(
|
||||
AppendQueryParameters(Azure::Core::Url(queueClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
InitStorageClientOptions<Queues::QueueClientOptions>());
|
||||
EXPECT_NO_THROW(queueClient1.GetProperties());
|
||||
|
||||
{
|
||||
Queues::GetUserDelegationKeyOptions options;
|
||||
options.DelegatedUserTid = "00000000-0000-0000-0000-000000000000";
|
||||
userDelegationKey = queueServiceClient.GetUserDelegationKey(sasExpiresOn, options).Value;
|
||||
}
|
||||
sasToken = queueSasBuilder.GenerateSasToken(userDelegationKey, accountName);
|
||||
Queues::QueueClient queueClient2(
|
||||
AppendQueryParameters(Azure::Core::Url(queueClient.GetUrl()), sasToken),
|
||||
endUserCredential,
|
||||
InitStorageClientOptions<Queues::QueueClientOptions>());
|
||||
EXPECT_THROW(queueClient2.GetProperties(), StorageException);
|
||||
}
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user