Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit 5abda06

Browse files
committed
Add support for user delegation SAS
1 parent a86cce3 commit 5abda06

21 files changed

+558
-183
lines changed

Microsoft.WindowsAzure.Storage/includes/was/auth.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ namespace azure { namespace storage {
2626
class cloud_blob_shared_access_headers;
2727
class cloud_file_shared_access_headers;
2828
class account_shared_access_policy;
29+
struct user_delegation_key;
2930

3031
}} // namespace azure::storage
3132

3233
namespace azure { namespace storage { namespace protocol {
3334

34-
utility::string_t calculate_hmac_sha256_hash(const utility::string_t& string_to_hash, const storage_credentials& credentials);
35+
utility::string_t calculate_hmac_sha256_hash(const utility::string_t& string_to_hash, const std::vector<uint8_t>& key);
3536

3637
const utility::string_t auth_name_shared_key(_XPLATSTR("SharedKey"));
3738
const utility::string_t auth_name_shared_key_lite(_XPLATSTR("SharedKeyLite"));
@@ -474,6 +475,7 @@ namespace azure { namespace storage { namespace protocol {
474475
utility::string_t get_queue_sas_token(const utility::string_t& identifier, const shared_access_policy& policy, const utility::string_t& resource, const storage_credentials& credentials);
475476
utility::string_t get_table_sas_token(const utility::string_t& identifier, const shared_access_policy& policy, const utility::string_t& table_name, const utility::string_t& start_partition_key, const utility::string_t& start_row_key, const utility::string_t& end_partition_key, const utility::string_t& end_row_key, const utility::string_t& resource, const storage_credentials& credentials);
476477
utility::string_t get_file_sas_token(const utility::string_t& identifier, const shared_access_policy& policy, const cloud_file_shared_access_headers& headers, const utility::string_t& resource_type, const utility::string_t& resource, const storage_credentials& credentials);
478+
utility::string_t get_blob_user_delegation_sas_token(const shared_access_policy& policy, const cloud_blob_shared_access_headers& headers, const utility::string_t& resource_type, const utility::string_t& resource, const utility::string_t& snapshot_time, const user_delegation_key& key);
477479
storage_credentials parse_query(const web::http::uri& uri, bool require_signed_resource);
478480

479481
#pragma endregion

Microsoft.WindowsAzure.Storage/includes/was/blob.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,6 +2516,17 @@ namespace azure { namespace storage {
25162516
friend class protocol::list_containers_reader;
25172517
};
25182518

2519+
struct user_delegation_key
2520+
{
2521+
utility::string_t signed_oid;
2522+
utility::string_t signed_tid;
2523+
utility::datetime signed_start;
2524+
utility::datetime signed_expiry;
2525+
utility::string_t signed_service;
2526+
utility::string_t signed_version;
2527+
utility::string_t key;
2528+
};
2529+
25192530
/// <summary>
25202531
/// Provides a client-side logical representation of the Windows Azure Blob Service. This client is used to configure and execute requests against the Blob Service.
25212532
/// </summary>
@@ -3064,6 +3075,39 @@ namespace azure { namespace storage {
30643075
m_directory_delimiter = std::move(value);
30653076
}
30663077

3078+
/// <summary>
3079+
/// Gets a key that can be used to sign a user delegation SAS (shared access signature).
3080+
/// </summary>
3081+
/// <param name="start">The start time for the user delegation key.</param>
3082+
/// <param name="expiry">The expiry time for the user delegation key.</param>
3083+
/// <returns>A string containing user delegation key.</returns>
3084+
user_delegation_key get_user_delegation_key(const utility::datetime& start, const utility::datetime& expiry)
3085+
{
3086+
return get_user_delegation_key_async(start, expiry).get();
3087+
}
3088+
3089+
/// <summary>
3090+
/// Initiates an asynchronous operation to get a key that can be used to sign a user delegation SAS (shared access signature).
3091+
/// </summary>
3092+
/// <param name="start">The start time for the user delegation key.</param>
3093+
/// <param name="expiry">The expiry time for the user delegation key.</param>
3094+
/// <returns>A <see cref="pplx::task" /> object of string that contains user delegation key.</returns>
3095+
pplx::task<user_delegation_key> get_user_delegation_key_async(const utility::datetime& start, const utility::datetime& expiry)
3096+
{
3097+
return get_user_delegation_key_async(start, expiry, blob_request_options(), operation_context(), pplx::cancellation_token::none());
3098+
}
3099+
3100+
/// <summary>
3101+
/// Initiates an asynchronous operation to get a key that can be used to sign a user delegation SAS (shared access signature).
3102+
/// </summary>
3103+
/// <param name="start">The start time for the user delegation key.</param>
3104+
/// <param name="expiry">The expiry time for the user delegation key.</param>
3105+
/// <param name="options">An <see cref="azure::storage::blob_request_options" /> object that specifies additional options for the request.</param>
3106+
/// <param name="context">An <see cref="azure::storage::operation_context" /> object that represents the context for the current operation.</param>
3107+
/// <param name="cancellation_token">An <see cref="pplx::cancellation_token" /> object that is used to cancel the current operation.</param>
3108+
/// <returns>A <see cref="pplx::task" /> object of string that contains user delegation key.</returns>
3109+
WASTORAGE_API pplx::task<user_delegation_key> get_user_delegation_key_async(const utility::datetime& start, const utility::datetime& expiry, const request_options& modified_options, operation_context context, const pplx::cancellation_token& cancellation_token);
3110+
30673111
private:
30683112
pplx::task<account_properties> download_account_properties_base_async(const storage_uri& uri, const request_options& modified_options, operation_context context, const pplx::cancellation_token& cancellation_token) const;
30693113

@@ -3175,6 +3219,14 @@ namespace azure { namespace storage {
31753219
/// <returns>A string containing a shared access signature.</returns>
31763220
WASTORAGE_API utility::string_t get_shared_access_signature(const blob_shared_access_policy& policy, const utility::string_t& stored_policy_identifier) const;
31773221

3222+
/// <summary>
3223+
/// Returns a user delegation SAS for the container.
3224+
/// </summary>
3225+
/// <param name="key">User delegation key used to sign this SAS.</param>
3226+
/// <param name="policy">The access policy for the shared access signature.</param>
3227+
/// <returns>A string containing a shared access signature.</returns>
3228+
WASTORAGE_API utility::string_t get_user_delegation_sas(const user_delegation_key& key, const blob_shared_access_policy& policy) const;
3229+
31783230
/// <summary>
31793231
/// Gets a reference to a blob in this container.
31803232
/// </summary>
@@ -4612,6 +4664,27 @@ namespace azure { namespace storage {
46124664
/// <returns>A string containing a shared access signature.</returns>
46134665
WASTORAGE_API utility::string_t get_shared_access_signature(const blob_shared_access_policy& policy, const utility::string_t& stored_policy_identifier, const cloud_blob_shared_access_headers& headers) const;
46144666

4667+
4668+
/// <summary>
4669+
/// Returns a user delegation SAS for the blob.
4670+
/// </summary>
4671+
/// <param name="key">User delegation key used to sign this SAS.</param>
4672+
/// <param name="policy">The access policy for the shared access signature.</param>
4673+
/// <returns>A string containing a shared access signature.</returns>
4674+
utility::string_t get_user_delegation_sas(const user_delegation_key& key, const blob_shared_access_policy& policy) const
4675+
{
4676+
return get_user_delegation_sas(key, policy, cloud_blob_shared_access_headers());
4677+
}
4678+
4679+
/// <summary>
4680+
/// Returns a user delegation SAS for the blob.
4681+
/// </summary>
4682+
/// <param name="key">User delegation key used to sign this SAS.</param>
4683+
/// <param name="policy">The access policy for the shared access signature.</param>
4684+
/// <param name="headers">The optional header values to set for a blob returned with this SAS.</param>
4685+
/// <returns>A string containing a shared access signature.</returns>
4686+
WASTORAGE_API utility::string_t get_user_delegation_sas(const user_delegation_key& key, const blob_shared_access_policy& policy, const cloud_blob_shared_access_headers& headers) const;
4687+
46154688
/// <summary>
46164689
/// Opens a stream for reading from the blob.
46174690
/// </summary>

Microsoft.WindowsAzure.Storage/includes/was/core.h

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,22 +232,45 @@ namespace azure { namespace storage {
232232
{
233233
}
234234

235+
class sas_credential
236+
{
237+
public:
238+
explicit sas_credential(utility::string_t sas_token) : m_sas_token(std::move(sas_token))
239+
{
240+
}
241+
242+
private:
243+
utility::string_t m_sas_token;
244+
245+
friend class storage_credentials;
246+
};
247+
235248
/// <summary>
236249
/// Initializes a new instance of the <see cref="azure::storage::storage_credentials" /> class with the specified shared access signature token.
237250
/// </summary>
238251
/// <param name="sas_token">A string containing the shared access signature token.</param>
239252
explicit storage_credentials(utility::string_t sas_token)
240-
: m_sas_token(std::move(sas_token))
253+
: storage_credentials(utility::string_t(), sas_credential{sas_token})
254+
{
255+
}
256+
257+
/// <summary>
258+
/// Initializes a new instance of the <see cref="azure::storage::storage_credentials" /> class with the specified account name and shared access signature token.
259+
/// </summary>
260+
/// <param name="account_name">A string containing the name of the storage account.</param>
261+
/// <param name="sas_token">An <see cref="azure::storage::sas_credential" /> containing shared access signature token.</param>
262+
storage_credentials(utility::string_t account_name, sas_credential sas_token)
263+
: m_account_name(std::move(account_name)), m_sas_token(std::move(sas_token.m_sas_token))
241264
{
242265
if (m_sas_token.size() >= 1 && m_sas_token.at(0) == _XPLATSTR('?'))
243266
{
244267
m_sas_token = m_sas_token.substr(1);
245268
}
246-
269+
247270
auto splitted_query = web::uri::split_query(m_sas_token);
248271
if (!splitted_query.empty())
249272
{
250-
splitted_query[protocol::uri_query_sas_api_version] = protocol::header_value_storage_version;
273+
splitted_query[protocol::uri_query_sas_api_version] = protocol::header_value_storage_version;
251274
web::uri_builder builder;
252275
for (const auto& kv : splitted_query)
253276
{
@@ -278,7 +301,17 @@ namespace azure { namespace storage {
278301
/// </summary>
279302
/// <param name="token">A <see cref="azure::storage::storage_credentials::bearer_token_credential" /> class containing bearer token.</param>
280303
template<class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, bearer_token_credential>::value>::type* = nullptr>
281-
explicit storage_credentials(T&& token) : m_bearer_token_credential(std::make_shared<bearer_token_credential>())
304+
explicit storage_credentials(T&& token) : storage_credentials(utility::string_t(), std::forward<T>(token))
305+
{
306+
}
307+
308+
/// <summary>
309+
/// Initializes a new instance of the <see cref="azure::storage::storage_credentials" /> class with the specified account name and bearer token.
310+
/// </summary>
311+
/// <param name="account_name">A string containing the name of the storage account.</param>
312+
/// <param name="token">A <see cref="azure::storage::storage_credentials::bearer_token_credential" /> class containing bearer token.</param>
313+
template<class T, typename std::enable_if<std::is_same<typename std::decay<T>::type, bearer_token_credential>::value>::type* = nullptr>
314+
storage_credentials(utility::string_t account_name, T&& token) : m_account_name(std::move(account_name)), m_bearer_token_credential(std::make_shared<bearer_token_credential>())
282315
{
283316
m_bearer_token_credential->m_bearer_token = std::forward<T>(token).m_bearer_token;
284317
}
@@ -399,7 +432,7 @@ namespace azure { namespace storage {
399432
/// <returns><c>true</c> if the credentials are for anonymous access; otherwise, <c>false</c>.</returns>
400433
bool is_anonymous() const
401434
{
402-
return m_sas_token.empty() && m_account_name.empty() && !is_bearer_token();
435+
return m_sas_token.empty() && m_account_key.empty() && !is_bearer_token();
403436
}
404437

405438
/// <summary>
@@ -408,7 +441,7 @@ namespace azure { namespace storage {
408441
/// <returns><c>true</c> if the credentials are a shared access signature token; otherwise, <c>false</c>.</returns>
409442
bool is_sas() const
410443
{
411-
return !m_sas_token.empty() && m_account_name.empty() && !is_bearer_token();
444+
return !m_sas_token.empty() && m_account_key.empty() && !is_bearer_token();
412445
}
413446

414447
/// <summary>
@@ -417,7 +450,7 @@ namespace azure { namespace storage {
417450
/// <returns><c>true</c> if the credentials are a shared key; otherwise, <c>false</c>.</returns>
418451
bool is_shared_key() const
419452
{
420-
return m_sas_token.empty() && !m_account_name.empty() && !is_bearer_token();
453+
return m_sas_token.empty() && !m_account_key.empty() && !is_bearer_token();
421454
}
422455

423456
/// <summary>

Microsoft.WindowsAzure.Storage/includes/wascore/constants.dat

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ DAT(uri_query_sas_services, _XPLATSTR("ss"))
6565
DAT(uri_query_sas_resource_types, _XPLATSTR("srt"))
6666
DAT(uri_query_sas_ip, _XPLATSTR("sip"))
6767
DAT(uri_query_sas_protocol, _XPLATSTR("spr"))
68+
DAT(uri_query_sas_skoid, _XPLATSTR("skoid"))
69+
DAT(uri_query_sas_sktid, _XPLATSTR("sktid"))
70+
DAT(uri_query_sas_skt, _XPLATSTR("skt"))
71+
DAT(uri_query_sas_ske, _XPLATSTR("ske"))
72+
DAT(uri_query_sas_sks, _XPLATSTR("sks"))
73+
DAT(uri_query_sas_skv, _XPLATSTR("skv"))
6874

6975
// table query parameters
7076
DAT(table_query_next_partition_key, _XPLATSTR("NextPartitionKey"))
@@ -106,6 +112,7 @@ DAT(component_range, _XPLATSTR("range"))
106112
DAT(component_incrementalcopy, _XPLATSTR("incrementalcopy"))
107113
DAT(component_tier, _XPLATSTR("tier"))
108114
DAT(component_file_permission, _XPLATSTR("filepermission"))
115+
DAT(component_user_delegation_key, _XPLATSTR("userdelegationkey"))
109116

110117
// common resources
111118
DAT(root_container, _XPLATSTR("$root"))
@@ -375,6 +382,17 @@ DAT(xml_file_id, _XPLATSTR("Id"))
375382
DAT(xml_access_tier, _XPLATSTR("AccessTier"))
376383
DAT(xml_access_tier_inferred, _XPLATSTR("AccessTierInferred"))
377384
DAT(xml_access_tier_change_time, _XPLATSTR("AccessTierChangeTime"))
385+
DAT(xml_user_delegation_key, _XPLATSTR("UserDelegationKey"))
386+
DAT(xml_user_delegation_key_signed_oid, _XPLATSTR("SignedOid"))
387+
DAT(xml_user_delegation_key_signed_tid, _XPLATSTR("SignedTid"))
388+
DAT(xml_user_delegation_key_signed_start, _XPLATSTR("SignedStart"))
389+
DAT(xml_user_delegation_key_signed_expiry, _XPLATSTR("SignedExpiry"))
390+
DAT(xml_user_delegation_key_signed_service, _XPLATSTR("SignedService"))
391+
DAT(xml_user_delegation_key_signed_version, _XPLATSTR("SignedVersion"))
392+
DAT(xml_user_delegation_key_value, _XPLATSTR("Value"))
393+
DAT(xml_user_delegation_key_info, _XPLATSTR("KeyInfo"))
394+
DAT(xml_user_delegation_key_start, _XPLATSTR("Start"))
395+
DAT(xml_user_delegation_key_expiry, _XPLATSTR("Expiry"))
378396

379397
// json strings
380398
DAT(json_file_permission, _XPLATSTR("permission"))
@@ -411,6 +429,7 @@ DAT(error_crc64_mismatch, "Calculated CRC64 does not match existing property.")
411429
DAT(error_missing_md5, "MD5 does not exist. If you do not want to force validation, please disable use_transactional_md5.")
412430
DAT(error_missing_crc64, "CRC64 does not exist. If you do not want to force validation, please disable use_transactional_crc64.")
413431
DAT(error_sas_missing_credentials, "Cannot create Shared Access Signature unless Shared Key credentials are used.")
432+
DAT(error_uds_missing_credentials, "Cannot create User Delegation SAS unless token credentials are used")
414433
DAT(error_client_timeout, "The client could not finish the operation within specified timeout.")
415434
DAT(error_cannot_modify_snapshot, "Cannot perform this operation on a blob representing a snapshot.")
416435
DAT(error_page_blob_size_unknown, "The size of the page blob could not be determined, because a length argument is not provided and stream is not seekable or stream length exceeds the permitted length.")

Microsoft.WindowsAzure.Storage/includes/wascore/protocol.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace azure { namespace storage { namespace protocol {
7070
web::http::http_request abort_copy_blob(const utility::string_t& copy_id, const access_condition& condition, web::http::uri_builder& uri_builder, const std::chrono::seconds& timeout, operation_context context);
7171
web::http::http_request incremental_copy_blob(const web::http::uri& source, const access_condition& condition, const cloud_metadata& metadata, web::http::uri_builder& uri_builder, const std::chrono::seconds& timeout, operation_context context);
7272
web::http::http_request set_blob_tier(const utility::string_t& tier, const access_condition& condition, web::http::uri_builder& uri_builder, const std::chrono::seconds& timeout, operation_context context);
73+
web::http::http_request get_user_delegation_key(web::http::uri_builder& uri_builder, const std::chrono::seconds& timeout, operation_context context);
7374
void add_lease_id(web::http::http_request& request, const access_condition& condition);
7475
void add_sequence_number_condition(web::http::http_request& request, const access_condition& condition);
7576
void add_access_condition(web::http::http_request& request, const access_condition& condition);

Microsoft.WindowsAzure.Storage/includes/wascore/protocol_xml.h

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,12 +470,12 @@ namespace azure { namespace storage { namespace protocol {
470470

471471
if (policy.start().is_initialized())
472472
{
473-
write_element(xml_access_policy_start, core::convert_to_string_with_fixed_length_fractional_seconds(policy.start()));
473+
write_element(xml_access_policy_start, core::convert_to_iso8601_string(policy.start(), 7));
474474
}
475475

476476
if (policy.expiry().is_initialized())
477477
{
478-
write_element(xml_access_policy_expiry, core::convert_to_string_with_fixed_length_fractional_seconds(policy.expiry()));
478+
write_element(xml_access_policy_expiry, core::convert_to_iso8601_string(policy.expiry(), 7));
479479
}
480480

481481
if (policy.permission() != 0)
@@ -931,6 +931,40 @@ namespace azure { namespace storage { namespace protocol {
931931
void handle_geo_replication_status(const utility::string_t& element_name);
932932
};
933933

934+
class user_delegation_key_time_writer : public core::xml::xml_writer
935+
{
936+
public:
937+
938+
user_delegation_key_time_writer()
939+
{
940+
}
941+
942+
std::string write(const utility::datetime& start, const utility::datetime& expiry);
943+
};
944+
945+
class user_delegation_key_reader : public core::xml::xml_reader
946+
{
947+
public:
948+
explicit user_delegation_key_reader(concurrency::streams::istream stream) : xml_reader(stream)
949+
{
950+
}
951+
952+
user_delegation_key move_key()
953+
{
954+
auto result = parse();
955+
if (result == xml_reader::parse_result::xml_not_complete)
956+
{
957+
throw storage_exception(protocol::error_xml_not_complete, true);
958+
}
959+
return std::move(m_key);
960+
}
961+
962+
protected:
963+
void handle_element(const utility::string_t& element_name) override;
964+
965+
user_delegation_key m_key;
966+
};
967+
934968
}}} // namespace azure::storage::protocol
935969

936970
#pragma pop_macro("max")

0 commit comments

Comments
 (0)