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 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
@@ -0,0 +1,2 @@
1
+ = 0.1.0 - 25-Sep-2015
2
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,6 @@
1
+ CHANGES
2
+ MANIFEST
3
+ Rakefile
4
+ README
5
+ lib/azure/signature.rb
6
+ test/test_signature.rb
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