pandexio 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,78 +1,78 @@
1
- #Pandexio SDK for Ruby
2
-
3
- ##Overview
4
- This Pandexio SDK enables Ruby server applications to easily generate signed requests that can be consumed by the Pandexio REST API (https://platform.pandexio.com) and the Pandexio Hosted Model (https://hosted.pandexio.com).
5
-
6
- ##Definitions
7
-
8
- ###Pandexio::Request
9
- - type - class
10
- - attributes:
11
- - method - required. The request HTTP verb (GET, POST, PUT, PATCH, DELETE).
12
- - path - required. The path part of the request URL (e.g., /v2/documents/a4df7a95-d1f6-4664-b208-74568990bd15).
13
- - query_parameters - optional. A hash containing the query parameters (e.g., { "coverSize" => "xl" }).
14
- - headers - required. A hash containing the headers (e.g., { "Host" => "platform.pandexio.com" }). Host header must be included.
15
- - payload - optional. UTF-8 encoded request body. Only applicable for POST, PUT, and PATCH requests.
16
-
17
- ###Pandexio::SigningOptions
18
- - type - class
19
- - attributes:
20
- - algorithm - required. The HMAC algorithm to use. Choose from any specified in
21
- - Pandexio::SigningAlgorithms::PDX_HMAC_MD5 - MD5 HMAC
22
- - Pandexio::SigningAlgorithms::PDX_HMAC_SHA1 - SHA1 HMAC
23
- - Pandexio::SigningAlgorithms::PDX_HMAC_SHA256 - SHA256 HMAC
24
- - Pandexio::SigningAlgorithms::PDX_HMAC_SHA384 - SHA384 HMAC
25
- - Pandexio::SigningAlgorithms::PDX_HMAC_SHA512 - SHA512 HMAC
26
- - mechanism - required. Specifies which signing mechanism to use. Signing mechanisms include:
27
- - Pandexio::SigningMechanisms::QUERY_STRING - Sign the request using query string parameters
28
- - Pandexio::SigningMechanisms::HEADER - Sign the request using headers
29
- - domain_id - required. Public API key.
30
- - domain_key - required. Shared secret API key used to generate HMAC signatures.
31
- - date - required. ISO8601 date/time when the request was made.
32
- - expires - required. Number of seconds before the request signature expires.
33
- - originator - required. The name of the application, feature, etc. making the request.
34
- - email_address - required. The email address of the user making the request.
35
- - display_name - required. The display name of the user making the request.
36
- - profile_image - optional. The profile image thumbnail, either data URL or HTTP URL of the user making the request.
37
-
38
- ###Pandexio::to_authorized_request
39
- - type - method
40
- - arguments
41
- - Pandexio::Request *normalized_request* - A non-signed request containing information about the request to be made.
42
- - Pandexio::SigningOptions *signing_options* - The details specifying how to sign the *normalized_request*
43
- - result
44
- - Pandexio::Request *authorized_request* - A signed request containing information about the request to be made as well as signing information as headers or query string parameters, depending on the *signing_options*.
45
-
46
- ##Signing a Request
47
- Take the following steps to create a signed Pandexio request.
48
-
49
- 1. Create an instance of Pandexio::Request containing details of the request to be made, the *normalized_request*.
50
- 2. Create an instance of Pandexio::SigningOptions. These *signing_options* specify the SDK will sign the request.
51
- 3. Call Pandexio::to_authorized_request, passing in the *normalized_request* and the *signing_options*.
52
- 4. Build an HTTP request using the Pandexio::Request, *authorized_request*, returned by Pandexio::to_authorized_request and make the HTTP request using the HTTP client of choice.
53
-
54
- ##All Together
55
- Here's an example showing the generation of a signed Pandexio request to retrieve an extra large document cover thumbnail for a given document:
56
-
57
- ```ruby
58
- require 'pandexio'
59
-
60
- normalized_request = Pandexio::Request.new(
61
- :method => "GET",
62
- :path => "/v2/documents/a4df7a95-d1f6-4664-b208-74568990bd15/cover",
63
- :query_parameters => { "coverSize" => "xl" },
64
- :headers => { "Host" => "platform.pandexio.com" })
65
-
66
- signing_options = Pandexio::SigningOptions.new(
67
- :algorithm => Pandexio::SigningAlgorithms::PDX_HMAC_SHA256,
68
- :mechanism => Pandexio::SigningMechanisms::QUERY_STRING,
69
- :domain_id => "1234567890",
70
- :domain_key => "asdfjklqwerzxcv",
71
- :date => Time.utc(2015, 6, 10, 13, 22, 46),
72
- :expires => 90,
73
- :originator => "Demo",
74
- :email_address => "terry@contoso.com",
75
- :display_name => "Terry Contoso")
76
-
77
- authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
78
- ```
1
+ #Pandexio SDK for Ruby
2
+
3
+ ##Overview
4
+ This Pandexio SDK enables Ruby server applications to easily generate signed requests that can be consumed by the Pandexio REST API (https://platform.pandexio.com) and the Pandexio Hosted Model (https://hosted.pandexio.com).
5
+
6
+ ##Definitions
7
+
8
+ ###Pandexio::Request
9
+ - type - class
10
+ - attributes:
11
+ - method - required. The request HTTP verb (GET, POST, PUT, PATCH, DELETE).
12
+ - path - required. The path part of the request URL (e.g., /v2/documents/a4df7a95-d1f6-4664-b208-74568990bd15).
13
+ - query_parameters - optional. A hash containing the query parameters (e.g., { "coverSize" => "xl" }).
14
+ - headers - required. A hash containing the headers (e.g., { "Host" => "platform.pandexio.com" }). Host header must be included.
15
+ - payload - optional. UTF-8 encoded request body. Only applicable for POST, PUT, and PATCH requests.
16
+
17
+ ###Pandexio::SigningOptions
18
+ - type - class
19
+ - attributes:
20
+ - algorithm - required. The HMAC algorithm to use. Choose from any specified in
21
+ - Pandexio::SigningAlgorithms::PDX_HMAC_MD5 - MD5 HMAC
22
+ - Pandexio::SigningAlgorithms::PDX_HMAC_SHA1 - SHA1 HMAC
23
+ - Pandexio::SigningAlgorithms::PDX_HMAC_SHA256 - SHA256 HMAC
24
+ - Pandexio::SigningAlgorithms::PDX_HMAC_SHA384 - SHA384 HMAC
25
+ - Pandexio::SigningAlgorithms::PDX_HMAC_SHA512 - SHA512 HMAC
26
+ - mechanism - required. Specifies which signing mechanism to use. Signing mechanisms include:
27
+ - Pandexio::SigningMechanisms::QUERY_STRING - Sign the request using query string parameters
28
+ - Pandexio::SigningMechanisms::HEADER - Sign the request using headers
29
+ - domain_id - required. Public API key.
30
+ - domain_key - required. Shared secret API key used to generate HMAC signatures.
31
+ - date - required. ISO8601 date/time when the request was made.
32
+ - expires - required. Number of seconds before the request signature expires.
33
+ - originator - required. The name of the application, feature, etc. making the request.
34
+ - email_address - required. The email address of the user making the request.
35
+ - display_name - required. The display name of the user making the request.
36
+ - profile_image - optional. The profile image thumbnail, either data URL or HTTP URL of the user making the request.
37
+
38
+ ###Pandexio::to_authorized_request
39
+ - type - method
40
+ - arguments
41
+ - Pandexio::Request *normalized_request* - A non-signed request containing information about the request to be made.
42
+ - Pandexio::SigningOptions *signing_options* - The details specifying how to sign the *normalized_request*
43
+ - result
44
+ - Pandexio::Request *authorized_request* - A signed request containing information about the request to be made as well as signing information as headers or query string parameters, depending on the *signing_options*.
45
+
46
+ ##Signing a Request
47
+ Take the following steps to create a signed Pandexio request.
48
+
49
+ 1. Create an instance of Pandexio::Request containing details of the request to be made, the *normalized_request*.
50
+ 2. Create an instance of Pandexio::SigningOptions. These *signing_options* specify the SDK will sign the request.
51
+ 3. Call Pandexio::to_authorized_request, passing in the *normalized_request* and the *signing_options*.
52
+ 4. Build an HTTP request using the Pandexio::Request, *authorized_request*, returned by Pandexio::to_authorized_request and make the HTTP request using the HTTP client of choice.
53
+
54
+ ##All Together
55
+ Here's an example showing the generation of a signed Pandexio request to retrieve an extra large document cover thumbnail for a given document:
56
+
57
+ ```ruby
58
+ require 'pandexio'
59
+
60
+ normalized_request = Pandexio::Request.new(
61
+ :method => "GET",
62
+ :path => "/v2/documents/a4df7a95-d1f6-4664-b208-74568990bd15/cover",
63
+ :query_parameters => { "coverSize" => "xl" },
64
+ :headers => { "Host" => "platform.pandexio.com" })
65
+
66
+ signing_options = Pandexio::SigningOptions.new(
67
+ :algorithm => Pandexio::SigningAlgorithms::PDX_HMAC_SHA256,
68
+ :mechanism => Pandexio::SigningMechanisms::QUERY_STRING,
69
+ :domain_id => "1234567890",
70
+ :domain_key => "asdfjklqwerzxcv",
71
+ :date => Time.utc(2015, 6, 10, 13, 22, 46),
72
+ :expires => 90,
73
+ :originator => "Demo",
74
+ :email_address => "terry@contoso.com",
75
+ :display_name => "Terry Contoso")
76
+
77
+ authorized_request = Pandexio::to_authorized_request(normalized_request, signing_options)
78
+ ```
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
- require 'rake/testtask'
2
-
3
- Rake::TestTask.new do |t|
4
- t.libs << 'test'
5
- t.verbose = true
6
- end
7
-
8
- desc "Run tests"
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.verbose = true
6
+ end
7
+
8
+ desc "Run tests"
9
9
  task :default => :test
@@ -1,156 +1,15 @@
1
- require 'time'
2
- require 'stringio'
3
- require 'openssl'
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.to_a, b[0].codepoints.to_a
20
-
21
- min = [a_codepoints.length, b_codepoints.length].min - 1
22
-
23
- for i in 0..min
24
- a_codepoint = a_codepoints[i]
25
- b_codepoint = b_codepoints[i]
26
- c = a_codepoint <=> b_codepoint
27
- return c if c != 0
28
- end
29
-
30
- return a_codepoints.length <=> b_codepoints.length
31
-
32
- end
33
-
34
- def self.build_canonical_query_string(query_parameters)
35
-
36
- temp_query_parameters = query_parameters.dup
37
-
38
- temp_query_parameters = temp_query_parameters.sort { |a, b| ordinal_key_value_sort(a, b) }
39
-
40
- canonical_query_string = StringIO.new
41
-
42
- temp_query_parameters.each do |key, value|
43
- next if key == SigningAttributes::ALGORITHM || key == SigningAttributes::CREDENTIAL || key == SigningAttributes::SIGNED_HEADERS || key == SigningAttributes::SIGNATURE
44
- canonical_query_string << "&" if canonical_query_string.length > 0
45
- canonical_query_string << "#{key}=#{value}"
46
- end
47
-
48
- return canonical_query_string.string
49
-
50
- end
51
-
52
- def self.build_canonical_headers(headers)
53
-
54
- temp_headers = {}
55
-
56
- headers.each do |key, value|
57
- next if key == SigningAttributes::AUTHORIZATION
58
- temp_headers[key.downcase.strip] = value
59
- end
60
-
61
- temp_headers = temp_headers.sort { |a, b| ordinal_key_value_sort(a, b) }
62
-
63
- canonical_headers, signed_headers = StringIO.new, StringIO.new
64
-
65
- temp_headers.each do |key, value|
66
- next if key == SigningAttributes::AUTHORIZATION
67
- canonical_headers << LINE_BREAK if canonical_headers.length > 0
68
- canonical_headers << "#{key}:#{value}"
69
- signed_headers << ";" if signed_headers.length > 0
70
- signed_headers << "#{key}"
71
- end
72
-
73
- return canonical_headers.string, signed_headers.string
74
-
75
- end
76
-
77
- def self.build_canonical_payload(payload, digest)
78
- canonical_payload = digest.hexdigest(payload).encode('UTF-8')
79
- return canonical_payload;
80
- end
81
-
82
- def self.build_canonical_request(request, digest)
83
- canonical_query_string = build_canonical_query_string(request.query_parameters)
84
- canonical_headers, signed_headers = build_canonical_headers(request.headers)
85
- canonical_payload = build_canonical_payload(request.payload, digest)
86
- 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}"
87
- return canonical_request, signed_headers
88
- end
89
-
90
- def self.build_string_to_sign(canonical_request, signing_options)
91
- signing_string = "#{signing_options.algorithm}#{LINE_BREAK}#{signing_options.date.iso8601}#{LINE_BREAK}#{canonical_request}".encode('UTF-8')
92
- return signing_string
93
- end
94
-
95
- def self.generate_signature(string_to_sign, signing_options, digest)
96
- return OpenSSL::HMAC.hexdigest(digest, signing_options.domain_key, string_to_sign)
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 = Pandexio::Request.new(
119
- :method => normalized_request.method,
120
- :path => normalized_request.path,
121
- :query_parameters => normalized_request.query_parameters.clone,
122
- :headers => normalized_request.headers.clone,
123
- :payload => normalized_request.payload)
124
-
125
- append = lambda { |p|
126
- p[SigningAttributes::DATE] = signing_options.date.iso8601
127
- p[SigningAttributes::EXPIRES] = signing_options.expires
128
- p[SigningAttributes::ORIGINATOR] = signing_options.originator
129
- p[SigningAttributes::EMAIL_ADDRESS] = signing_options.email_address
130
- p[SigningAttributes::DISPLAY_NAME] = signing_options.display_name
131
- p[SigningAttributes::PROFILE_IMAGE] = signing_options.profile_image if !signing_options.profile_image.nil? && signing_options.profile_image.is_a?(String) && !signing_options.profile_image.empty?
132
- }
133
-
134
- append.call(
135
- signing_options.mechanism == SigningMechanisms::QUERY_STRING ? authorized_request.query_parameters :
136
- signing_options.mechanism == SigningMechanisms::HEADER ? authorized_request.headers : {})
137
-
138
- digest = SigningAlgorithms.to_d(signing_options.algorithm)
139
- canonical_request, signed_headers = build_canonical_request(authorized_request, digest)
140
- string_to_sign = build_string_to_sign(canonical_request, signing_options)
141
- signature = generate_signature(string_to_sign, signing_options, digest)
142
-
143
- if signing_options.mechanism == SigningMechanisms::QUERY_STRING
144
- authorized_request.query_parameters[SigningAttributes::ALGORITHM] = signing_options.algorithm
145
- authorized_request.query_parameters[SigningAttributes::CREDENTIAL] = signing_options.domain_id
146
- authorized_request.query_parameters[SigningAttributes::SIGNED_HEADERS] = signed_headers
147
- authorized_request.query_parameters[SigningAttributes::SIGNATURE] = signature
148
- elsif signing_options.mechanism == SigningMechanisms::HEADER
149
- authorized_request.headers[SigningAttributes::AUTHORIZATION] = "#{signing_options.algorithm} Credential=#{signing_options.domain_id}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
150
- end
151
-
152
- return authorized_request
153
-
154
- end
155
-
1
+ require_relative 'signer.rb'
2
+
3
+ module Pandexio
4
+
5
+ def self.to_authorized_request(normalized_request, signing_options)
6
+
7
+ signer = Pandexio::Signer.new()
8
+
9
+ authorized_request = signer.sign(normalized_request, signing_options)
10
+
11
+ return authorized_request
12
+
13
+ end
14
+
156
15
  end
@@ -1,25 +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
-
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
25
  end
@@ -0,0 +1,13 @@
1
+ module Pandexio
2
+
3
+ class Scope
4
+
5
+ def initialize(params = {})
6
+ @document_ids = params.fetch(:document_ids, [])
7
+ end
8
+
9
+ attr_accessor :document_ids
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,7 @@
1
+ module Pandexio
2
+
3
+ class ScopePatterns
4
+ DOCUMENT_PATH_PATTERN = /(\/.*?documents\/)(?<documentid>[a-zA-Z0-9\-_]+)/i
5
+ end
6
+
7
+ end
@@ -0,0 +1,192 @@
1
+ require 'time'
2
+ require 'stringio'
3
+ require 'openssl'
4
+ require_relative 'request.rb'
5
+ require_relative 'scope.rb'
6
+ require_relative 'scope_patterns.rb'
7
+ require_relative 'signing_algorithms.rb'
8
+ require_relative 'signing_attributes.rb'
9
+ require_relative 'signing_mechanisms.rb'
10
+ require_relative 'signing_options.rb'
11
+
12
+ module Pandexio
13
+
14
+ class Signer
15
+
16
+ LINE_BREAK = "\r\n"
17
+ private_constant :LINE_BREAK
18
+
19
+ private
20
+
21
+ def extract_scope(normalized_request)
22
+ scope = Pandexio::Scope.new()
23
+
24
+ match = normalized_request.path.match(Pandexio::ScopePatterns::DOCUMENT_PATH_PATTERN)
25
+ if !match.nil?
26
+ document_id = match["documentid"]
27
+ scope.document_ids.push(document_id)
28
+ end
29
+
30
+ normalized_request.query_parameters.each do |key, value|
31
+ next if key.casecmp('documentids') != 0
32
+ document_ids = value.split(',')
33
+ scope.document_ids.concat(document_ids)
34
+ end
35
+
36
+ return scope
37
+ end
38
+
39
+ def ordinal_key_value_sort(a, b)
40
+
41
+ a_codepoints, b_codepoints = a[0].codepoints.to_a, b[0].codepoints.to_a
42
+
43
+ min = [a_codepoints.length, b_codepoints.length].min - 1
44
+
45
+ for i in 0..min
46
+ a_codepoint = a_codepoints[i]
47
+ b_codepoint = b_codepoints[i]
48
+ c = a_codepoint <=> b_codepoint
49
+ return c if c != 0
50
+ end
51
+
52
+ return a_codepoints.length <=> b_codepoints.length
53
+
54
+ end
55
+
56
+ def build_canonical_query_string(query_parameters)
57
+
58
+ temp_query_parameters = query_parameters.sort { |a, b| ordinal_key_value_sort(a, b) }
59
+
60
+ canonical_query_string = StringIO.new
61
+
62
+ temp_query_parameters.each do |key, value|
63
+ next if key.casecmp(SigningAttributes::ALGORITHM) == 0 ||
64
+ key.casecmp(SigningAttributes::CREDENTIAL) == 0 ||
65
+ key.casecmp(SigningAttributes::SIGNED_HEADERS) == 0 ||
66
+ key.casecmp(SigningAttributes::SIGNATURE) == 0
67
+ canonical_query_string << "&" if canonical_query_string.length > 0
68
+ canonical_query_string << "#{key}=#{value}"
69
+ end
70
+
71
+ return canonical_query_string.string
72
+
73
+ end
74
+
75
+ def build_canonical_headers(headers)
76
+
77
+ temp_headers = {}
78
+
79
+ headers.each do |key, value|
80
+ next if key.casecmp(SigningAttributes::AUTHORIZATION) == 0
81
+ temp_headers[key.downcase.strip] = value
82
+ end
83
+
84
+ temp_headers = temp_headers.sort { |a, b| ordinal_key_value_sort(a, b) }
85
+
86
+ canonical_headers, signed_headers = StringIO.new, StringIO.new
87
+
88
+ temp_headers.each do |key, value|
89
+ canonical_headers << LINE_BREAK if canonical_headers.length > 0
90
+ canonical_headers << "#{key}:#{value}"
91
+ signed_headers << ";" if signed_headers.length > 0
92
+ signed_headers << "#{key}"
93
+ end
94
+
95
+ return canonical_headers.string, signed_headers.string
96
+
97
+ end
98
+
99
+ def build_canonical_payload(payload, digest)
100
+ canonical_payload = digest.hexdigest(payload).encode('UTF-8')
101
+ return canonical_payload;
102
+ end
103
+
104
+ def build_canonical_request(request, digest)
105
+ canonical_query_string = build_canonical_query_string(request.query_parameters)
106
+ canonical_headers, signed_headers = build_canonical_headers(request.headers)
107
+ canonical_payload = build_canonical_payload(request.payload, digest)
108
+ 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}"
109
+ return canonical_request, signed_headers
110
+ end
111
+
112
+ def build_string_to_sign(canonical_request, signing_options)
113
+ signing_string = "#{signing_options.algorithm}#{LINE_BREAK}#{signing_options.date.iso8601}#{LINE_BREAK}#{canonical_request}".encode('UTF-8')
114
+ return signing_string
115
+ end
116
+
117
+ def generate_signature(string_to_sign, signing_options, digest)
118
+ return OpenSSL::HMAC.hexdigest(digest, signing_options.domain_key, string_to_sign)
119
+ end
120
+
121
+ def build_authorized_request(normalized_request, signing_options)
122
+ authorized_request = Pandexio::Request.new(
123
+ :method => normalized_request.method,
124
+ :path => normalized_request.path,
125
+ :query_parameters => normalized_request.query_parameters.clone,
126
+ :headers => normalized_request.headers.clone,
127
+ :payload => normalized_request.payload)
128
+
129
+ append = lambda { |p|
130
+ p[SigningAttributes::DATE] = signing_options.date.iso8601
131
+ p[SigningAttributes::EXPIRES] = signing_options.expires
132
+ p[SigningAttributes::ORIGINATOR] = signing_options.originator
133
+ p[SigningAttributes::EMAIL_ADDRESS] = signing_options.email_address
134
+ p[SigningAttributes::DISPLAY_NAME] = signing_options.display_name
135
+ p[SigningAttributes::PROFILE_IMAGE] = signing_options.profile_image if !signing_options.profile_image.nil? && signing_options.profile_image.is_a?(String) && !signing_options.profile_image.empty?
136
+ }
137
+
138
+ append.call(
139
+ signing_options.mechanism == SigningMechanisms::QUERY_STRING ? authorized_request.query_parameters :
140
+ signing_options.mechanism == SigningMechanisms::HEADER ? authorized_request.headers : {})
141
+
142
+ digest = SigningAlgorithms.to_d(signing_options.algorithm)
143
+ canonical_request, signed_headers = build_canonical_request(authorized_request, digest)
144
+ string_to_sign = build_string_to_sign(canonical_request, signing_options)
145
+ signature = generate_signature(string_to_sign, signing_options, digest)
146
+
147
+ if signing_options.mechanism == SigningMechanisms::QUERY_STRING
148
+ authorized_request.query_parameters[SigningAttributes::ALGORITHM] = signing_options.algorithm
149
+ authorized_request.query_parameters[SigningAttributes::CREDENTIAL] = signing_options.domain_id
150
+ authorized_request.query_parameters[SigningAttributes::SIGNED_HEADERS] = signed_headers
151
+ authorized_request.query_parameters[SigningAttributes::SIGNATURE] = signature
152
+ elsif signing_options.mechanism == SigningMechanisms::HEADER
153
+ authorized_request.headers[SigningAttributes::AUTHORIZATION] = "#{signing_options.algorithm} Credential=#{signing_options.domain_id}, SignedHeaders=#{signed_headers}, Signature=#{signature}"
154
+ end
155
+
156
+ return authorized_request
157
+ end
158
+
159
+ public
160
+
161
+ def sign(normalized_request, signing_options)
162
+ scope, authorized_request = scope_and_sign(normalized_request, signing_options)
163
+ return authorized_request
164
+ end
165
+
166
+ def scope_and_sign(normalized_request, signing_options)
167
+
168
+ raise ArgumentError, 'normalized_request must be of type Pandexio::Request and cannot be nil' unless !normalized_request.nil? && normalized_request.is_a?(Request)
169
+ 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)
170
+ 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)
171
+
172
+ raise ArgumentError, 'signing_options must be of type Pandexio::SigningOptions cannot be nil' unless !signing_options.nil? && signing_options.is_a?(SigningOptions)
173
+ 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?
174
+ 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?
175
+ raise ArgumentError, 'signing_options.algorithm must be of type String and cannot be nil or empty' unless SigningAlgorithms.is_v(signing_options.algorithm)
176
+ raise ArgumentError, 'signing_options.mechanism must be a valid signing mechanism' unless SigningMechanisms.is_v(signing_options.mechanism)
177
+ 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)
178
+ 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
179
+ 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?
180
+ 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?
181
+ 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?
182
+
183
+ scope = extract_scope(normalized_request)
184
+ authorized_request = build_authorized_request(normalized_request, signing_options)
185
+
186
+ return scope, authorized_request
187
+
188
+ end
189
+
190
+ end
191
+
192
+ end