azure-signature 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +2 -0
- data/MANIFEST +6 -0
- data/README +46 -0
- data/Rakefile +27 -0
- data/azure-signature.gemspec +23 -0
- data/lib/azure/signature.rb +131 -0
- data/test/test_signature.rb +90 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3e4bf37dea120788f3e4b8b1f1f4ba2cac0da67
|
4
|
+
data.tar.gz: 804102d0275a2c4a7bb01dfda9636a85e32529dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3593818f612766c12b61de678db9f4766866855e17f936dd88e2fa3367cf7ba33b49557dc4f6b9925e7607bb4ae04e565d3053f4828a6f7e2814b0e79d16688d
|
7
|
+
data.tar.gz: ad442a9a682c1d1a921a37bd5c1baf073b1555c00bcd6df310d0d44236e4b351e703b3d057c7a19361fe21d7e59398ba3f792fa374f9baf8363d2d7f5fa5dc67
|
data/CHANGES
ADDED
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= Description
|
2
|
+
A Ruby library for generating an authentication signature for Azure storage services.
|
3
|
+
|
4
|
+
= Installation
|
5
|
+
gem install azure-signature
|
6
|
+
|
7
|
+
= Synopis
|
8
|
+
require 'azure/signature'
|
9
|
+
|
10
|
+
key = "SGVsbG8gV29ybGQ="
|
11
|
+
url = "http://testsnapshots.blob.core.windows.net/Tables"
|
12
|
+
|
13
|
+
sig = Azure::Signature.new(url, key)
|
14
|
+
|
15
|
+
# Look at canonical URL
|
16
|
+
p sig.canonical_url # => "/testsnapshots/Tables"
|
17
|
+
|
18
|
+
# Get a signature with the defaults
|
19
|
+
p sig.signature(:table)
|
20
|
+
|
21
|
+
# Or pass some options
|
22
|
+
p sig.signature(:table, :auth_string => true, :date => some_date, :verb => 'PUT')
|
23
|
+
|
24
|
+
= Caveats
|
25
|
+
For the first release only table signatures are supported (because that's
|
26
|
+
what I happened to need). I'll add blob, file and queue in the future.
|
27
|
+
|
28
|
+
= Acknowledgements
|
29
|
+
I borrowed the code to canonicalize resources and headers from the
|
30
|
+
azure-sdk-for-ruby project.
|
31
|
+
|
32
|
+
= Copyright
|
33
|
+
Copyright (c) 2015, Daniel J. Berger.
|
34
|
+
All rights reserved.
|
35
|
+
|
36
|
+
= License
|
37
|
+
Apache 2.0
|
38
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
39
|
+
|
40
|
+
= Warranty
|
41
|
+
This package is provided "as is" and without any express or
|
42
|
+
implied warranties, including, without limitation, the implied
|
43
|
+
warranties of merchantability and fitness for a particular purpose.
|
44
|
+
|
45
|
+
= Author
|
46
|
+
Daniel Berger
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
CLEAN.include("**/*.gem", "**/*.rbc")
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc 'Create the azure-signature gem'
|
9
|
+
task :create => [:clean] do
|
10
|
+
require 'rubygems/package'
|
11
|
+
spec = eval(IO.read('azure-signature.gemspec'))
|
12
|
+
Gem::Package.build(spec)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Install the azure-signature library as a gem"
|
16
|
+
task :install => [:create] do
|
17
|
+
file = Dir["*.gem"].first
|
18
|
+
sh "gem install -l #{file}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::TestTask.new do |t|
|
23
|
+
t.warning = true
|
24
|
+
t.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
task :default => :test
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'azure-signature'
|
5
|
+
gem.version = '0.1.0'
|
6
|
+
gem.author = 'Daniel J. Berger'
|
7
|
+
gem.license = 'Apache 2.0'
|
8
|
+
gem.email = 'djberg96@gmail.com'
|
9
|
+
gem.homepage = 'http://github.com/djberg96/azure-signature'
|
10
|
+
gem.summary = 'Generate authentication signatures for Azure'
|
11
|
+
gem.test_file = 'test/test_signature.rb'
|
12
|
+
gem.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
13
|
+
|
14
|
+
gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
15
|
+
|
16
|
+
gem.add_development_dependency('test-unit')
|
17
|
+
|
18
|
+
gem.description = <<-EOF
|
19
|
+
The azure-signature library generates storage signatures for
|
20
|
+
Microsoft Azure's cloud platform. You can use this to access
|
21
|
+
Azure storage services - tables, blobs, queues and files.
|
22
|
+
EOF
|
23
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'openssl'
|
3
|
+
require 'base64'
|
4
|
+
require 'cgi'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
# The Azure module serves as a namespace.
|
8
|
+
module Azure
|
9
|
+
# The Signature class encapsulates an canonicalized resource string.
|
10
|
+
class Signature
|
11
|
+
# The version of the azure-signature library.
|
12
|
+
VERSION = '0.1.0'
|
13
|
+
|
14
|
+
# The resource (URL) passed to the constructor.
|
15
|
+
attr_reader :resource
|
16
|
+
|
17
|
+
# The canonical version of the resource.
|
18
|
+
attr_reader :canonical_resource
|
19
|
+
|
20
|
+
# The base64-decoded account key passed to the constructor.
|
21
|
+
attr_reader :key
|
22
|
+
|
23
|
+
# The name of the storage account, based on the resource.
|
24
|
+
attr_reader :account_name
|
25
|
+
|
26
|
+
# A URI object that encapsulates the resource.
|
27
|
+
attr_reader :uri
|
28
|
+
|
29
|
+
alias url resource
|
30
|
+
alias canonical_url canonical_resource
|
31
|
+
|
32
|
+
# Creates and returns an Azure::Signature object taking a +resource+ (URL)
|
33
|
+
# as an argument and a storage account key. The +resource+ will typically
|
34
|
+
# be an Azure storage account endpoint.
|
35
|
+
#
|
36
|
+
def initialize(resource, key)
|
37
|
+
@resource = resource
|
38
|
+
@uri = URI.parse(resource)
|
39
|
+
@account_name = @uri.host.split(".").first.split("-").first
|
40
|
+
@key = Base64.strict_decode64(key)
|
41
|
+
@canonical_resource = canonicalize_resource(resource)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Generate a signature for use with the table service. Use the +options+
|
45
|
+
# hash to pass optional information. The following keys are supported:
|
46
|
+
#
|
47
|
+
# - :auth_type. Either 'SharedKey' (the default) or 'SharedKeyLight'.
|
48
|
+
# - :verb. The http verb used for SharedKey auth. The default is 'GET'.
|
49
|
+
# - :date. The date (or x-ms-date) used. The default is Time.now.httpdate.
|
50
|
+
# - :content_md5. The Content-MD5 if desired. The default is nil.
|
51
|
+
# - :content_type. The Content-Type if desired. The default is nil.
|
52
|
+
# - :auth_string. If true, prepends the auth_type + account name to the
|
53
|
+
# result and returns a string. The default is false.
|
54
|
+
#
|
55
|
+
# The result is a digest string that you can use as an authorization header
|
56
|
+
# for future http requests to (presumably) Azure storage endpoints.
|
57
|
+
#
|
58
|
+
def table_signature(options = {})
|
59
|
+
auth_type = options[:auth_type] || 'SharedKey'
|
60
|
+
verb = options[:verb] || 'GET'
|
61
|
+
date = options[:date] || Time.now.httpdate
|
62
|
+
auth_string = options[:auth_string] || false
|
63
|
+
content_md5 = options[:content_md5]
|
64
|
+
content_type = options[:content_type]
|
65
|
+
|
66
|
+
unless ['SharedKey', 'SharedKeyLight'].include?(auth_type)
|
67
|
+
raise ArgumentError, "auth type must be SharedKey or SharedKeyLight"
|
68
|
+
end
|
69
|
+
|
70
|
+
if auth_type == 'SharedKey'
|
71
|
+
body = [verb, content_md5, content_type, date, canonical_resource].join("\n")
|
72
|
+
else
|
73
|
+
body = [date, canonical_resource].join("\n")
|
74
|
+
end
|
75
|
+
|
76
|
+
if auth_string
|
77
|
+
"Authorization: #{auth_type} #{account_name}:" + sign(body)
|
78
|
+
else
|
79
|
+
sign(body)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generic wrapper method for getting a signature, where +type+ can be
|
84
|
+
# :table, :blob, :queue, or :file.
|
85
|
+
#
|
86
|
+
# At the moment only :table is supported.
|
87
|
+
#--
|
88
|
+
# TODO: Add support for other types.
|
89
|
+
#
|
90
|
+
def signature(type, args = {})
|
91
|
+
case type.to_s.downcase
|
92
|
+
when 'table'
|
93
|
+
table_signature(args)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Generate a canonical URL from an endpoint.
|
100
|
+
#--
|
101
|
+
# Borrowed from azure-sdk-for-ruby. I had my own, but this was nicer.
|
102
|
+
#
|
103
|
+
def canonicalize_resource(url)
|
104
|
+
resource = '/' + account_name + (uri.path.empty? ? '/' : uri.path)
|
105
|
+
params = CGI.parse(uri.query.to_s).map { |k,v| [k.downcase, v] }
|
106
|
+
params.sort_by! { |k,v| k }
|
107
|
+
params.map! { |k,v| '%s:%s' % [k, v.map(&:strip).sort.join(',')] }
|
108
|
+
[resource, *params].join("\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
# Generate canonical headers.
|
112
|
+
#--
|
113
|
+
# Borrowed from azure-sdk-for-ruby.
|
114
|
+
#
|
115
|
+
def canonicalized_headers(headers)
|
116
|
+
headers = headers.map { |k,v| [k.to_s.downcase, v] }
|
117
|
+
headers.select! { |k,v| k =~ /^x-ms-/ }
|
118
|
+
headers.sort_by! { |k,v| k }
|
119
|
+
headers.map! { |k,v| '%s:%s' % [k, v] }
|
120
|
+
headers.map! { |h| h.gsub(/\s+/, ' ') }.join("\n")
|
121
|
+
end
|
122
|
+
|
123
|
+
# Generate a digest based on the +data+ argument, using the key
|
124
|
+
# passed to constructor.
|
125
|
+
#
|
126
|
+
def sign(body)
|
127
|
+
signed = OpenSSL::HMAC.digest('sha256', key, body)
|
128
|
+
Base64.strict_encode64(signed)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test-unit'
|
2
|
+
require 'azure/signature'
|
3
|
+
|
4
|
+
class TC_Azure_Signature < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@key = "SGVsbG8gV29ybGQ="
|
7
|
+
@url = 'http://testsnapshots.blob.core.windows.net/?comp=list'
|
8
|
+
@sig = Azure::Signature.new(@url, @key)
|
9
|
+
end
|
10
|
+
|
11
|
+
test "version constant is set to expected value" do
|
12
|
+
assert_equal("0.1.0", Azure::Signature::VERSION)
|
13
|
+
end
|
14
|
+
|
15
|
+
test "key method basic functionality" do
|
16
|
+
assert_respond_to(@sig, :key)
|
17
|
+
assert_kind_of(String, @sig.key)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "account_name basic functionality" do
|
21
|
+
assert_respond_to(@sig, :account_name)
|
22
|
+
assert_equal('testsnapshots', @sig.account_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "resource method basic functionality" do
|
26
|
+
assert_respond_to(@sig, :resource)
|
27
|
+
assert_equal(@url, @sig.resource)
|
28
|
+
end
|
29
|
+
|
30
|
+
test "url is an alias for the resource method" do
|
31
|
+
assert_respond_to(@sig, :url)
|
32
|
+
assert_alias_method(@sig, :url, :resource)
|
33
|
+
end
|
34
|
+
|
35
|
+
test "uri method basic functionality" do
|
36
|
+
assert_respond_to(@sig, :uri)
|
37
|
+
assert_kind_of(URI, @sig.uri)
|
38
|
+
end
|
39
|
+
|
40
|
+
test "canonical_resource method basic functionality" do
|
41
|
+
assert_respond_to(@sig, :canonical_resource)
|
42
|
+
assert_kind_of(String, @sig.canonical_resource)
|
43
|
+
end
|
44
|
+
|
45
|
+
test "canonical_url is an alias for the canonical_resource method" do
|
46
|
+
assert_respond_to(@sig, :canonical_url)
|
47
|
+
assert_alias_method(@sig, :canonical_url, :canonical_resource)
|
48
|
+
end
|
49
|
+
|
50
|
+
test "canonical_resource returns the expected value for basic url" do
|
51
|
+
@url = "http://myaccount.blob.core.windows.net/Tables"
|
52
|
+
@sig = Azure::Signature.new(@url, @key)
|
53
|
+
expected = "/myaccount/Tables"
|
54
|
+
assert_equal(expected, @sig.canonical_resource)
|
55
|
+
end
|
56
|
+
|
57
|
+
test "canonical_resource returns the expected value for url with query" do
|
58
|
+
@url = "http://myaccount.blob.core.windows.net/mycontainer?restype=container&comp=metadata"
|
59
|
+
@sig = Azure::Signature.new(@url, @key)
|
60
|
+
expected = "/myaccount/mycontainer\ncomp:metadata\nrestype:container"
|
61
|
+
assert_equal(expected, @sig.canonical_resource)
|
62
|
+
end
|
63
|
+
|
64
|
+
test "canonical_resource returns the expected value for url with multiple, identical query params" do
|
65
|
+
@url = "http://myaccount.blob.core.windows.net/mycontainer?restype=container"
|
66
|
+
@url << "&comp=list&include=snapshots&include=metadata&include=uncommittedblobs"
|
67
|
+
@sig = Azure::Signature.new(@url, @key)
|
68
|
+
expected = "/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,"
|
69
|
+
expected << "uncommittedblobs\nrestype:container"
|
70
|
+
assert_equal(expected, @sig.canonical_resource)
|
71
|
+
end
|
72
|
+
|
73
|
+
test "canonical_resource returns the expected value for secondary account" do
|
74
|
+
@url = "https://myaccount-secondary.blob.core.windows.net/mycontainer/myblob"
|
75
|
+
@sig = Azure::Signature.new(@url, @key)
|
76
|
+
expected = "/myaccount/mycontainer/myblob"
|
77
|
+
assert_equal(expected, @sig.canonical_resource)
|
78
|
+
end
|
79
|
+
|
80
|
+
test "constructor requires two arguments" do
|
81
|
+
assert_raise(ArgumentError){ Azure::Signature.new }
|
82
|
+
assert_raise(ArgumentError){ Azure::Signature.new('http://foo/bar') }
|
83
|
+
end
|
84
|
+
|
85
|
+
def teardown
|
86
|
+
@key = nil
|
87
|
+
@url = nil
|
88
|
+
@sig = nil
|
89
|
+
end
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: azure-signature
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel J. Berger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: test-unit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: |2
|
28
|
+
The azure-signature library generates storage signatures for
|
29
|
+
Microsoft Azure's cloud platform. You can use this to access
|
30
|
+
Azure storage services - tables, blobs, queues and files.
|
31
|
+
email: djberg96@gmail.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files:
|
35
|
+
- README
|
36
|
+
- CHANGES
|
37
|
+
- MANIFEST
|
38
|
+
files:
|
39
|
+
- CHANGES
|
40
|
+
- MANIFEST
|
41
|
+
- README
|
42
|
+
- Rakefile
|
43
|
+
- azure-signature.gemspec
|
44
|
+
- lib/azure/signature.rb
|
45
|
+
- test/test_signature.rb
|
46
|
+
homepage: http://github.com/djberg96/azure-signature
|
47
|
+
licenses:
|
48
|
+
- Apache 2.0
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 2.4.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Generate authentication signatures for Azure
|
70
|
+
test_files:
|
71
|
+
- test/test_signature.rb
|