pandexio 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []