Skip to content

Commit

Permalink
Merge 3e29ade into df9c2cb
Browse files Browse the repository at this point in the history
  • Loading branch information
c-w committed Jan 28, 2020
2 parents df9c2cb + 3e29ade commit c53a019
Show file tree
Hide file tree
Showing 36 changed files with 351 additions and 30 deletions.
9 changes: 4 additions & 5 deletions .travis.yml
Expand Up @@ -2,11 +2,10 @@
language: ruby

rvm:
- 2.0.0
- 2.1.6
- 2.2.2
- 2.4.1
- 2.5.0
- 2.4
- 2.5
- 2.6
- 2.7

before_install:
- gem install bundler
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -27,6 +27,7 @@ source "https://rubygems.org" do
gem "azure-core", "~> 0.1.13", :require => false
gem "nokogiri", "~> 1.6", ">= 1.6.8", :require => false

gem "adal", "~> 1.0", :require => false
gem "dotenv", "~> 2.0", :require => false
gem "minitest", "~> 5", :require => false
gem "minitest-reporters", "~> 1", :require => false
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -15,7 +15,7 @@ This project provides Ruby packages that makes it easy to access and manage Micr
Note:

* x64 Ruby for Windows is known to have some compatibility issues.
* Each service gems depends on gem nokogiri. For Ruby version lower than 2.2, please install the compatible nokogiri before trying to install azure-storage.
* Each service gems depends on gem nokogiri.

# Getting Started for Contributors

Expand Down
4 changes: 4 additions & 0 deletions blob/BreakingChanges.md
@@ -1,3 +1,7 @@
Tracking Breaking Changes in 1.2.0

* This module now supports Ruby versions to 2.4 through 2.7

Tracking Breaking Changes in 1.0.0

* This module now only consists of functionalities to access Azure Storage Blob Service.
Expand Down
4 changes: 4 additions & 0 deletions blob/ChangeLog.md
@@ -1,3 +1,7 @@
2020.2 - version 1.2.0
* This module now supports Ruby versions to 2.4 through 2.7
* Add support for generating user delegation shared access signatures.

2018.11 - version 1.1.0
* Added the support for sending a request with a bearer token.

Expand Down
4 changes: 2 additions & 2 deletions blob/README.md
Expand Up @@ -9,12 +9,12 @@ This project provides a Ruby package that makes it easy to access and manage Mic

# Supported Ruby Versions

* Ruby 1.9.3 to 2.5
* Ruby 2.4 to 2.7

Note:

* x64 Ruby for Windows is known to have some compatibility issues.
* azure-storage-blob depends on gem nokogiri. For Ruby version lower than 2.2, please install the compatible nokogiri before trying to install azure-storage-blob.
* azure-storage-blob depends on gem nokogiri.

# Getting Started

Expand Down
4 changes: 2 additions & 2 deletions blob/azure-storage-blob.gemspec
Expand Up @@ -38,10 +38,10 @@ Gem::Specification.new do |s|
s.license = "MIT"
s.files = `git ls-files ./lib/azure/storage/blob/`.split("\n") << "./lib/azure/storage/blob.rb"

s.required_ruby_version = ">= 1.9.3"
s.required_ruby_version = ">= 2.4.0"

s.add_runtime_dependency("azure-core", "~> 0.1.13")
s.add_runtime_dependency("azure-storage-common", "~> 1.0")
s.add_runtime_dependency("azure-storage-common", "~> 1.2")
s.add_runtime_dependency("nokogiri", "~> 1.6", ">= 1.6.8")

s.add_development_dependency("dotenv", "~> 2.0")
Expand Down
41 changes: 41 additions & 0 deletions blob/lib/azure/storage/blob/blob_service.rb
Expand Up @@ -254,6 +254,33 @@ def list_containers(options = {})
Serialization.container_enumeration_results_from_xml(response.body)
end

# Public: Obtain a user delegation key for the purpose of signing SAS tokens.
#
# ==== Attributes
#
# * +start+ - Time. The start time for the user delegation SAS.
# * +expiry+ - Time. The expiry time of user delegation SAS.
#
# See: https://docs.microsoft.com/en-us/rest/api/storageservices/get-user-delegation-key
#
# NOTE: A token credential must be present on the service object for this request to succeed.
# The start and expiry times must be within 7 days of the current time.
#
# Returns an Azure::Storage::Common::UserDelegationKey
#
def get_user_delegation_key(start, expiry)
max_delegation_time = Time.now + BlobConstants::MAX_USER_DELEGATION_KEY_SECONDS
raise ArgumentError, "Start time must be before #{max_delegation_time}" if start > max_delegation_time
raise ArgumentError, "Expiry time must be before #{max_delegation_time}" if expiry > max_delegation_time
raise ArgumentError, "Start time must be before expiry time" if start >= expiry

body = Serialization.key_info_to_xml(start, expiry)

response = call(:post, user_delegation_key_uri, body)

Serialization.user_delegation_key_from_xml(response.body)
end

# Protected: Establishes an exclusive write lock on a container or a blob. The lock duration can be 15 to 60 seconds, or can be infinite.
# To write to a locked container or blob, a client must provide a lease ID.
#
Expand Down Expand Up @@ -577,6 +604,20 @@ def containers_uri(query = {}, options = {})
generate_uri("", query, options)
end

# Protected: Generate the URI for the user delegation key.
#
# ==== Attributes
#
# * +query+ - A Hash of key => value query parameters.
#
# Returns a URI.
#
protected
def user_delegation_key_uri(query = {}, options = {})
query = { :restype => "service", :comp => "userdelegationkey" }.merge(query)
generate_uri("", query, options)
end

# Protected: Generate the URI for a specific container.
#
# ==== Attributes
Expand Down
3 changes: 3 additions & 0 deletions blob/lib/azure/storage/blob/default.rb
Expand Up @@ -119,6 +119,9 @@ module BlobConstants
# The size of a page, in bytes, in a page blob.
PAGE_SIZE = 512

# The maximum validity of user delegation SAS (7 days from the current time).
MAX_USER_DELEGATION_KEY_SECONDS = 60 * 60 * 24 * 7

# Resource types.
module ResourceTypes
CONTAINER = "c"
Expand Down
25 changes: 25 additions & 0 deletions blob/lib/azure/storage/blob/serialization.rb
Expand Up @@ -49,6 +49,31 @@ def self.container_enumeration_results_from_xml(xml)
results
end

def self.key_info_to_xml(start, expiry)
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
xml.KeyInfo {
xml.Start start.utc.iso8601
xml.Expiry expiry.utc.iso8601
}
end
builder.to_xml
end

def self.user_delegation_key_from_xml(xml)
xml = slopify(xml)
expect_node("UserDelegationKey", xml)

Azure::Storage::Common::Service::UserDelegationKey.new do |user_delegation_key|
user_delegation_key.signed_oid = xml.SignedOid.text if (xml > "SignedOid").any?
user_delegation_key.signed_tid = xml.SignedTid.text if (xml > "SignedTid").any?
user_delegation_key.signed_start = xml.SignedStart.text if (xml > "SignedStart").any?
user_delegation_key.signed_expiry = xml.SignedExpiry.text if (xml > "SignedExpiry").any?
user_delegation_key.signed_service = xml.SignedService.text if (xml > "SignedService").any?
user_delegation_key.signed_version = xml.SignedVersion.text if (xml > "SignedVersion").any?
user_delegation_key.value = xml.Value.text if (xml > "Value").any?
end
end

def self.container_from_xml(xml)
xml = slopify(xml)
expect_node("Container", xml)
Expand Down
2 changes: 1 addition & 1 deletion blob/lib/azure/storage/blob/version.rb
Expand Up @@ -30,7 +30,7 @@ module Blob
class Version
# Fields represent the parts defined in http://semver.org/
MAJOR = 1 unless defined? MAJOR
MINOR = 1 unless defined? MINOR
MINOR = 2 unless defined? MINOR
UPDATE = 0 unless defined? UPDATE

class << self
Expand Down
4 changes: 4 additions & 0 deletions common/BreakingChanges.md
@@ -1,3 +1,7 @@
Tracking Breaking Changes in 1.2.0

* This module now supports Ruby versions to 2.4 through 2.7

Tracking Breaking Changes in 1.0.0

* This module now consists of functionalities to support service client library modules.
Expand Down
5 changes: 5 additions & 0 deletions common/ChangeLog.md
@@ -1,3 +1,8 @@
2020.2 - version 1.2.0
* This module now supports Ruby versions to 2.4 through 2.7
* Add support for generating user delegation shared access signatures.
* Update the storage API version used to generate shared access signatures to 2018-11-09.

2018.11 - version 1.1.0
* Added the support for sending a request with a bearer token.
* Added the configuration for SSL versions.
Expand Down
4 changes: 2 additions & 2 deletions common/README.md
Expand Up @@ -8,12 +8,12 @@ This project provides a Ruby package that supports service client libraries.

# Supported Ruby Versions

* Ruby 1.9.3 to 2.5
* Ruby 2.4 to 2.7

Note:

* x64 Ruby for Windows is known to have some compatibility issues.
* azure-storage-common depends on gem nokogiri. For Ruby version lower than 2.2, please install the compatible nokogiri before trying to install azure-storage.
* azure-storage-common depends on gem nokogiri.

# Getting Started

Expand Down
2 changes: 1 addition & 1 deletion common/azure-storage-common.gemspec
Expand Up @@ -38,7 +38,7 @@ Gem::Specification.new do |s|
s.license = "MIT"
s.files = `git ls-files ./lib/azure/storage/common/`.split("\n") << "./lib/azure/storage/common.rb"

s.required_ruby_version = ">= 1.9.3"
s.required_ruby_version = ">= 2.4.0"

s.add_runtime_dependency("azure-core", "~> 0.1.13")
s.add_runtime_dependency("nokogiri", "~> 1.6", ">= 1.6.8")
Expand Down
1 change: 1 addition & 0 deletions common/lib/azure/storage/common/autoload.rb
Expand Up @@ -55,6 +55,7 @@ module Service
autoload :StorageService, "azure/storage/common/service/storage_service"
autoload :CorsRule, "azure/storage/common/service/cors_rule"
autoload :EnumerationResults, "azure/storage/common/service/enumeration_results"
autoload :UserDelegationKey, "azure/storage/common/service/user_delegation_key"
end
end
end
Expand Down
Expand Up @@ -67,8 +67,18 @@ class SharedAccessSignature
ip_range: :sip
}

USER_DELEGATION_KEY_MAPPINGS = {
signed_oid: :skoid,
signed_tid: :sktid,
signed_start: :skt,
signed_expiry: :ske,
signed_service: :sks,
signed_version: :skv
}

BLOB_KEY_MAPPINGS = {
resource: :sr,
timestamp: :snapshot,
cache_control: :rscc,
content_disposition: :rscd,
content_encoding: :rsce,
Expand Down Expand Up @@ -103,13 +113,20 @@ class SharedAccessSignature
#
# @param account_name [String] The account name. Defaults to the one in the global configuration.
# @param access_key [String] The access_key encoded in Base64. Defaults to the one in the global configuration.
def initialize(account_name = "", access_key = "")
# @param user_delegation_key [Azure::Storage::Common::UserDelegationKey] The user delegation key obtained from
# calling get_user_delegation_key after authenticating with an Azure Active Directory entity. If present, the
# SAS is signed with the user delegation key instead of the access key.
def initialize(account_name = "", access_key = "", user_delegation_key = nil)
if access_key.empty? && !user_delegation_key.nil?
access_key = user_delegation_key.value
end
if account_name.empty? || access_key.empty?
client = Azure::Storage::Common::Client.create_from_env
account_name = client.storage_account_name if account_name.empty?
access_key = client.storage_access_key if access_key.empty?
end
@account_name = account_name
@user_delegation_key = user_delegation_key
@signer = Azure::Core::Auth::Signer.new(access_key)
end

Expand All @@ -131,10 +148,12 @@ def initialize(account_name = "", access_key = "")
# * +:start+ - String. Optional. UTC Date/Time in ISO8601 format.
# * +:expiry+ - String. Optional. UTC Date/Time in ISO8601 format. Default now + 30 minutes.
# * +:identifier+ - String. Optional. Identifier for stored access policy.
# This option must be omitted if a user delegation key has been provided.
# * +:protocol+ - String. Optional. Permitted protocols.
# * +:ip_range+ - String. Optional. An IP address or a range of IP addresses from which to accept requests.
#
# Below options for blob serivce only
# * +:snapshot+ - String. Optional. UTC Date/Time in ISO8601 format. The blob snapshot to grant permission.
# * +:cache_control+ - String. Optional. Response header override.
# * +:content_disposition+ - String. Optional. Response header override.
# * +:content_encoding+ - String. Optional. Response header override.
Expand Down Expand Up @@ -175,12 +194,20 @@ def generate_service_sas_token(path, options = {})
valid_mappings.merge!(FILE_KEY_MAPPINGS)
end

service_key_mappings = SERVICE_KEY_MAPPINGS
unless @user_delegation_key.nil?
valid_mappings.delete(:identifier)
USER_DELEGATION_KEY_MAPPINGS.each { |k, _| options[k] = @user_delegation_key.send(k) }
valid_mappings.merge!(USER_DELEGATION_KEY_MAPPINGS)
service_key_mappings = service_key_mappings.merge(USER_DELEGATION_KEY_MAPPINGS)
end

invalid_options = options.reject { |k, _| valid_mappings.key?(k) }
raise Azure::Storage::Common::InvalidOptionsError, "invalid options #{invalid_options} provided for SAS token generate" if invalid_options.length > 0

canonicalize_time(options)

query_hash = Hash[options.map { |k, v| [SERVICE_KEY_MAPPINGS[k], v] }]
query_hash = Hash[options.map { |k, v| [service_key_mappings[k], v] }]
.reject { |k, v| SERVICE_OPTIONAL_QUERY_PARAMS.include?(k) && v.to_s == "" }
.merge(sig: @signer.sign(signable_string_for_service(service_type, path, options)))

Expand All @@ -197,13 +224,33 @@ def signable_string_for_service(service_type, path, options)
options[:permissions],
options[:start],
options[:expiry],
canonicalized_resource(service_type, path),
options[:identifier],
canonicalized_resource(service_type, path)
]

if @user_delegation_key.nil?
signable_fields.push(options[:identifier])
else
signable_fields.concat [
@user_delegation_key.signed_oid,
@user_delegation_key.signed_tid,
@user_delegation_key.signed_start,
@user_delegation_key.signed_expiry,
@user_delegation_key.signed_service,
@user_delegation_key.signed_version
]
end

signable_fields.concat [
options[:ip_range],
options[:protocol],
Azure::Storage::Common::Default::STG_VERSION
]

signable_fields.concat [
options[:resource],
options[:timestamp]
] if service_type == Azure::Storage::Common::ServiceType::BLOB

signable_fields.concat [
options[:cache_control],
options[:content_disposition],
Expand Down
2 changes: 1 addition & 1 deletion common/lib/azure/storage/common/default.rb
Expand Up @@ -30,7 +30,7 @@
module Azure::Storage::Common
module Default
# Default REST service (STG) version number. This is used only for SAS generator.
STG_VERSION = "2017-11-09"
STG_VERSION = "2018-11-09"

# The number of default concurrent requests for parallel operation.
DEFAULT_PARALLEL_OPERATION_THREAD_COUNT = 1
Expand Down

0 comments on commit c53a019

Please sign in to comment.