pandexio 0.0.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: d09ed0b91e468d7feba7093a009224a310e77766
4
+ data.tar.gz: ac4d71c496c6769bc4b95a0e7514597ef3c0df7a
5
+ SHA512:
6
+ metadata.gz: 48d13f7f4055315ad4ff67efd8d6ceecbe83355f1acf27ba695af5618389902c67ba606d6018d9bcbcc0fed9847c1352aa62b687c431916fb31b823cde364174
7
+ data.tar.gz: 44d1a250babf03e9083bcac1f2f53b73477879563d056b0ef2c9d062aea05b5707d4f598c9c89ac069b85c10404ceb7eaa608d2246a829ef831d7585b7a27592
data/lib/module.rb ADDED
@@ -0,0 +1,151 @@
1
+ require 'time'
2
+ require 'stringio'
3
+ require 'digest/hmac'
4
+ require_relative 'request.rb'
5
+ require_relative 'signing_algorithms.rb'
6
+ require_relative 'signing_attributes.rb'
7
+ require_relative 'signing_mechanisms.rb'
8
+ require_relative 'signing_options.rb'
9
+
10
+ module Pandexio
11
+
12
+ LINE_BREAK = "\r\n"
13
+ private_constant :LINE_BREAK
14
+
15
+ private
16
+
17
+ def self.ordinal_key_value_sort(a, b)
18
+
19
+ a_codepoints, b_codepoints = a[0].codepoints, b[0].codepoints
20
+
21
+ max_i = [a_codepoints.size, b_codepoints.size].min
22
+
23
+ for i in 0..max_i
24
+ a_codepoint = a_codepoints[i]
25
+ b_codepoint = b_codepoints[i]
26
+ return -1 if a_codepoint < b_codepoint
27
+ return 1 if a_codepoint > b_codepoint
28
+ end
29
+
30
+ return 0
31
+
32
+ end
33
+
34
+ def self.build_canonical_query_string(query_parameters)
35
+
36
+ temp_query_parameters = query_parameters.dup
37
+
38
+ query_sort = ->(a,b) { ordinal_key_value_sort(a, b) }
39
+ temp_query_parameters = temp_query_parameters.sort(&query_sort)
40
+
41
+ canonical_query_string = StringIO.new
42
+
43
+ temp_query_parameters.each do |key, value|
44
+ next if key == SigningAttributes::ALGORITHM || key == SigningAttributes::CREDENTIAL || key == SigningAttributes::SIGNED_HEADERS || key == SigningAttributes::SIGNATURE
45
+ canonical_query_string << "&" if canonical_query_string.length > 0
46
+ canonical_query_string << "#{key}=#{value}"
47
+ end
48
+
49
+ return canonical_query_string.string
50
+
51
+ end
52
+
53
+ def self.build_canonical_headers(headers)
54
+
55
+ temp_headers = {}
56
+
57
+ headers.each do |key, value|
58
+ next if key == SigningAttributes::AUTHORIZATION
59
+ temp_headers[key.downcase.strip] = value
60
+ end
61
+
62
+ header_sort = ->(a,b) { ordinal_key_value_sort(a, b) }
63
+ temp_headers = temp_headers.sort(&header_sort)
64
+
65
+ canonical_headers, signed_headers = StringIO.new, StringIO.new
66
+
67
+ temp_headers.each do |key, value|
68
+ next if key == SigningAttributes::AUTHORIZATION
69
+ canonical_headers << LINE_BREAK if canonical_headers.length > 0
70
+ canonical_headers << "#{key}:#{value}"
71
+ signed_headers << ";" if signed_headers.length > 0
72
+ signed_headers << "#{key}"
73
+ end
74
+
75
+ return canonical_headers.string, signed_headers.string
76
+
77
+ end
78
+
79
+ def self.build_canonical_payload(payload, digest)
80
+ return digest.hexdigest(payload)
81
+ end
82
+
83
+ def self.build_canonical_request(request, digest)
84
+ canonical_query_string = build_canonical_query_string(request.query_parameters)
85
+ canonical_headers, signed_headers = build_canonical_headers(request.headers)
86
+ canonical_payload = build_canonical_payload(request.payload, digest)
87
+ canonical_request = "#{request.method}#{LINE_BREAK}#{request.path}#{LINE_BREAK}#{canonical_query_string}#{LINE_BREAK}#{canonical_headers}#{LINE_BREAK}#{signed_headers}#{LINE_BREAK}#{canonical_payload}"
88
+ return canonical_request, signed_headers
89
+ end
90
+
91
+ def self.build_string_to_sign(canonical_request, signing_options)
92
+ return "#{signing_options.algorithm}#{LINE_BREAK}#{signing_options.date.iso8601}#{LINE_BREAK}#{canonical_request}"
93
+ end
94
+
95
+ def self.generate_signature(string_to_sign, signing_options, digest)
96
+ return Digest::HMAC.hexdigest(string_to_sign, signing_options.domain_key, digest)
97
+ end
98
+
99
+ public
100
+
101
+ def self.to_authorized_request(normalized_request, signing_options)
102
+
103
+ raise ArgumentError, 'normalized_request must be of type Pandexio::Request and cannot be nil' unless !normalized_request.nil? && normalized_request.is_a?(Request)
104
+ raise ArgumentError, 'normalized_request.query_parameters must be of type Hash and cannot be nil' unless !normalized_request.query_parameters.nil? && normalized_request.query_parameters.is_a?(Hash)
105
+ raise ArgumentError, 'normalized_request.headers must be of type Hash and cannot be nil' unless !normalized_request.headers.nil? && normalized_request.headers.is_a?(Hash)
106
+
107
+ raise ArgumentError, 'signing_options must be of type Pandexio::SigningOptions cannot be nil' unless !signing_options.nil? && signing_options.is_a?(SigningOptions)
108
+ raise ArgumentError, 'signing_options.domain_id must be of type String and cannot be nil or empty' unless !signing_options.domain_id.nil? && signing_options.domain_id.is_a?(String) && !signing_options.domain_id.empty?
109
+ raise ArgumentError, 'signing_options.domain_key must be of type String and cannot be nil or empty' unless !signing_options.domain_key.nil? && signing_options.domain_key.is_a?(String) && !signing_options.domain_key.empty?
110
+ raise ArgumentError, 'signing_options.algorithm must be of type String and cannot be nil or empty' unless SigningAlgorithms.is_v(signing_options.algorithm)
111
+ raise ArgumentError, 'signing_options.mechanism must be a valid signing mechanism' unless SigningMechanisms.is_v(signing_options.mechanism)
112
+ raise ArgumentError, 'signing_options.date must be of type Time and cannot be nil' unless !signing_options.date.nil? && signing_options.date.is_a?(Time)
113
+ raise ArgumentError, 'signing_options.expires must be of type Fixnum and cannot be nil or empty' unless !signing_options.expires.nil? && signing_options.expires.is_a?(Fixnum) && signing_options.expires > 0
114
+ raise ArgumentError, 'signing_options.originator must be of type String and cannot be nil or empty' unless !signing_options.originator.nil? && signing_options.originator.is_a?(String) && !signing_options.originator.empty?
115
+ raise ArgumentError, 'signing_options.email_address must be of type String and cannot be nil or empty' unless !signing_options.email_address.nil? && signing_options.email_address.is_a?(String) && !signing_options.email_address.empty?
116
+ raise ArgumentError, 'signing_options.display_name must be of type String and cannot be nil or empty' unless !signing_options.display_name.nil? && signing_options.display_name.is_a?(String) && !signing_options.display_name.empty?
117
+
118
+ authorized_request = normalized_request.dup
119
+
120
+ append = -> (p) do
121
+ p[SigningAttributes::DATE] = signing_options.date.iso8601
122
+ p[SigningAttributes::EXPIRES] = signing_options.expires
123
+ p[SigningAttributes::ORIGINATOR] = signing_options.originator
124
+ p[SigningAttributes::EMAIL_ADDRESS] = signing_options.email_address
125
+ p[SigningAttributes::DISPLAY_NAME] = signing_options.display_name
126
+ p[SigningAttributes::THUMBNAIL] = signing_options.thumbnail if !signing_options.thumbnail.nil? && signing_options.thumbnail.is_a?(String) && !signing_options.thumbnail.empty?
127
+ end
128
+
129
+ append.call(
130
+ signing_options.mechanism == SigningMechanisms::QUERY_STRING ? authorized_request.query_parameters :
131
+ signing_options.mechanism == SigningMechanisms::HEADER ? authorized_request.headers : {})
132
+
133
+ digest = SigningAlgorithms.to_d(signing_options.algorithm)
134
+ canonical_request, signed_headers = build_canonical_request(authorized_request, digest)
135
+ string_to_sign = build_string_to_sign(canonical_request, signing_options)
136
+ signature = generate_signature(string_to_sign, signing_options, digest)
137
+
138
+ if signing_options.mechanism == SigningMechanisms::QUERY_STRING
139
+ authorized_request.query_parameters[SigningAttributes::ALGORITHM] = signing_options.algorithm
140
+ authorized_request.query_parameters[SigningAttributes::CREDENTIAL] = signing_options.domain_id
141
+ authorized_request.query_parameters[SigningAttributes::SIGNED_HEADERS] = signed_headers
142
+ authorized_request.query_parameters[SigningAttributes::SIGNATURE] = signature
143
+ elsif signing_options.mechanism == SigningMechanisms::HEADER
144
+ authorized_request.headers[SigningAttributes::AUTHORIZATION] = "#{signing_options.algorithm} Credential=#{signing_options.domain_id}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
145
+ end
146
+
147
+ return authorized_request
148
+
149
+ end
150
+
151
+ end
data/lib/request.rb ADDED
@@ -0,0 +1,25 @@
1
+ module Pandexio
2
+
3
+ class Request
4
+
5
+ def initialize(params = {})
6
+ @method = params.fetch(:method, nil)
7
+ @path = params.fetch(:path, nil)
8
+ @query_parameters = params.fetch(:query_parameters, {})
9
+ @headers = params.fetch(:headers, {})
10
+ @payload = params.fetch(:payload, nil)
11
+ end
12
+
13
+ attr_accessor :method
14
+ attr_accessor :path
15
+ attr_accessor :query_parameters
16
+ attr_accessor :headers
17
+ attr_accessor :payload
18
+
19
+ def to_s
20
+ "#{@method} #{@path}#{LINE_BREAK}query_parameters: #{query_parameters}#{LINE_BREAK}headers: #{headers}#{LINE_BREAK}payload: #{payload}"
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,37 @@
1
+ module Pandexio
2
+
3
+ class SigningAlgorithms
4
+
5
+ PDX_HMAC_MD5 = "PDX-HMAC-MD5"
6
+ PDX_HMAC_SHA1 = "PDX-HMAC-SHA1"
7
+ PDX_HMAC_SHA256 = "PDX-HMAC-SHA256"
8
+ PDX_HMAC_SHA384 = "PDX-HMAC-SHA384"
9
+ PDX_HMAC_SHA512 = "PDX-HMAC-SHA512"
10
+
11
+ def self.is_v(a)
12
+
13
+ return !a.nil? && a.is_a?(String) &&
14
+ (a == PDX_HMAC_MD5 ||
15
+ a == PDX_HMAC_SHA1 ||
16
+ a == PDX_HMAC_SHA256 ||
17
+ a == PDX_HMAC_SHA384 ||
18
+ a == PDX_HMAC_SHA512)
19
+
20
+ end
21
+
22
+ def self.to_d(a)
23
+
24
+ return case a
25
+ when SigningAlgorithms::PDX_HMAC_MD5; Digest::MD5
26
+ when SigningAlgorithms::PDX_HMAC_SHA1; Digest::SHA1
27
+ when SigningAlgorithms::PDX_HMAC_SHA256; Digest::SHA256
28
+ when SigningAlgorithms::PDX_HMAC_SHA384; Digest::SHA384
29
+ when SigningAlgorithms::PDX_HMAC_SHA512; Digest::SHA512
30
+ else raise 'Invalid signing algorithm'
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,22 @@
1
+ module Pandexio
2
+
3
+ class SigningAttributes
4
+ # Used by "headers" only
5
+ AUTHORIZATION = "Authorization"
6
+
7
+ # Used by "query_parameters" only
8
+ ALGORITHM = "X-Pdx-Algorithm"
9
+ CREDENTIAL = "X-Pdx-Credential"
10
+ SIGNED_HEADERS = "X-Pdx-SignedHeaders"
11
+ SIGNATURE = "X-Pdx-Signature"
12
+
13
+ # Used by "headers" and "query_parameters"
14
+ DATE = "X-Pdx-Date"
15
+ EXPIRES = "X-Pdx-Expires"
16
+ ORIGINATOR = "X-Pdx-Originator"
17
+ EMAIL_ADDRESS = "X-Pdx-EmailAddress"
18
+ DISPLAY_NAME = "X-Pdx-DisplayName"
19
+ THUMBNAIL = "X-Pdx-Thumbnail"
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ module Pandexio
2
+
3
+ class SigningMechanisms
4
+
5
+ QUERY_STRING = "QueryString"
6
+ HEADER = "Header"
7
+
8
+ def self.is_v(m)
9
+
10
+ return !m.nil? && m.is_a?(String) &&
11
+ (m == QUERY_STRING || m == HEADER)
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,30 @@
1
+ module Pandexio
2
+
3
+ class SigningOptions
4
+ def initialize(params = {})
5
+ @algorithm = params.fetch(:algorithm, nil)
6
+ @mechanism = params.fetch(:mechanism, nil)
7
+ @domain_id = params.fetch(:domain_id, nil)
8
+ @domain_key = params.fetch(:domain_key, nil)
9
+ @date = params.fetch(:date, nil)
10
+ @expires = params.fetch(:expires, nil)
11
+ @originator = params.fetch(:originator, nil)
12
+ @email_address = params.fetch(:email_address, nil)
13
+ @display_name = params.fetch(:display_name, nil)
14
+ @thumbnail = params.fetch(:thumbnail, nil)
15
+ end
16
+
17
+ attr_accessor :algorithm
18
+ attr_accessor :mechanism
19
+ attr_accessor :domain_id
20
+ attr_accessor :domain_key
21
+ attr_accessor :date
22
+ attr_accessor :expires
23
+ attr_accessor :originator
24
+ attr_accessor :email_address
25
+ attr_accessor :display_name
26
+ attr_accessor :thumbnail
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,35 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/module.rb'
3
+
4
+ describe Pandexio do
5
+ before do
6
+ normalized_request = Pandexio::Request.new(
7
+ :method => "PUT",
8
+ :path => "/asdf/qwer/1234/title",
9
+ :query_parameters => { "nonce" => "987654321", "Baseline" => "5" },
10
+ :headers => { "sample" => "example", "Host" => "localhost" },
11
+ :payload => "testing")
12
+
13
+ date = Time.utc(2014, 11, 21, 13, 43, 15)
14
+
15
+ signing_options = Pandexio::SigningOptions.new(
16
+ :algorithm => Pandexio::SigningAlgorithms::PDX_HMAC_SHA256,
17
+ :mechanism => Pandexio::SigningMechanisms::HEADER,
18
+ :domain_id => "1234567890",
19
+ :domain_key => "asdfjklqwerzxcv",
20
+ :date => date,
21
+ :expires => 90,
22
+ :originator => "HeaderSigningTest",
23
+ :email_address => "Anonymous",
24
+ :display_name => "Anonymous")
25
+
26
+ @authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
27
+ @authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
28
+ end
29
+
30
+ describe "#header_signing" do
31
+ it "returns the correct authorization header" do
32
+ @authorized_request.headers["Authorization"].must_equal "PDX-HMAC-SHA256 Credential=1234567890, SignedHeaders=host;sample;x-pdx-date;x-pdx-displayname;x-pdx-emailaddress;x-pdx-expires;x-pdx-originator, Signature=a2e3dbc31b712bec6071dc7c5770bc60d4b03afa20e8329e6f4f6a2d74d32709"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/module.rb'
3
+
4
+ describe Pandexio do
5
+ before do
6
+ normalized_request = Pandexio::Request.new(
7
+ :method => "PUT",
8
+ :path => "/asdf/qwer/1234/title",
9
+ :query_parameters => { "nonce" => "987654321", "Baseline" => "5" },
10
+ :headers => { "sample" => "example", "Host" => "localhost" },
11
+ :payload => "testing")
12
+
13
+ date = Time.utc(2014, 11, 21, 13, 43, 15)
14
+
15
+ signing_options = Pandexio::SigningOptions.new(
16
+ :algorithm => Pandexio::SigningAlgorithms::PDX_HMAC_SHA256,
17
+ :mechanism => Pandexio::SigningMechanisms::QUERY_STRING,
18
+ :domain_id => "1234567890",
19
+ :domain_key => "asdfjklqwerzxcv",
20
+ :date => date,
21
+ :expires => 90,
22
+ :originator => "QueryStringSigningTest",
23
+ :email_address => "Anonymous",
24
+ :display_name => "Anonymous")
25
+
26
+ @authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
27
+ @authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
28
+ end
29
+
30
+ describe "#query_string_signing" do
31
+ it "returns the correct algorithm as a query parameter" do
32
+ @authorized_request.query_parameters["X-Pdx-Algorithm"].must_equal "PDX-HMAC-SHA256"
33
+ end
34
+ it "returns the correct credential as a query parameter" do
35
+ @authorized_request.query_parameters["X-Pdx-Credential"].must_equal "1234567890"
36
+ end
37
+ it "returns the correct signed_headers value as a query parameter" do
38
+ @authorized_request.query_parameters["X-Pdx-SignedHeaders"].must_equal "host;sample"
39
+ end
40
+ it "returns the correct signature as a query parameter" do
41
+ @authorized_request.query_parameters["X-Pdx-Signature"].must_equal "6ab83c6a331ba2d684d2557f1e415f3aee86bee105da1f5ad1bc4cc1cdf42f1a"
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pandexio
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Varilone
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Pandexio SDK for Ruby
14
+ email: bvarilone@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/module.rb
20
+ - lib/request.rb
21
+ - lib/signing_algorithms.rb
22
+ - lib/signing_attributes.rb
23
+ - lib/signing_mechanisms.rb
24
+ - lib/signing_options.rb
25
+ - test/test_header_signing.rb
26
+ - test/test_query_string_signing.rb
27
+ homepage: http://rubygems.org/gems/pandexio
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.4.4
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: ''
51
+ test_files: []