freeclimb 4.1.4 → 4.2.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +28 -5
- data/lib/freeclimb/utils/request_verifier.rb +61 -0
- data/lib/freeclimb/utils/signature_information.rb +45 -0
- data/lib/freeclimb/version.rb +1 -1
- data/lib/freeclimb.rb +4 -0
- data/spec/utils/request_verifier_spec.rb +148 -0
- data/spec/utils/signature_information_spec.rb +38 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7826dfa439ee726cf91d308b8cd9eee895a48d53a425ac24926765525c23760
|
4
|
+
data.tar.gz: cec6eee4ac9ca13157b48a10a23f837c18f684bf3621cbe41fb89655291e493c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f6600bdf1b690008a0c6dfffb24de9e180e5e58600103e6e556dff32243fde20bd7f8d84ce5fcc1211804487fa82c68fd3f000b70bb1a4126278d4edbbcd537
|
7
|
+
data.tar.gz: c5e79a2cfbf443175d929f7b6ee271ae8a4c8202c93f7823cc8212a1a97ce0aff0b424f93a2ab022c63a3757cd2405bbfc81ab8b72de24bc7eb2835731d47e25
|
data/CHANGELOG.md
CHANGED
@@ -9,11 +9,20 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
9
9
|
|
10
10
|
None
|
11
11
|
|
12
|
+
<a name="4.2.0"></a>
|
13
|
+
|
14
|
+
## [4.2.0] - 2023-04-03
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Introduce signing secret verification class (RequestVerifier) - https://docs.freeclimb.com/docs/validating-requests-from-freeclimb#how-to-verify-requests-manually
|
19
|
+
|
12
20
|
<a name="4.1.4"></a>
|
13
21
|
|
14
22
|
## [4.1.4] - 2023-03-31
|
15
23
|
|
16
24
|
### Changed
|
25
|
+
|
17
26
|
- PerclScript.to_json serialization fixed
|
18
27
|
|
19
28
|
<a name="4.1.3"></a>
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ FreeClimb is a cloud-based application programming interface (API) that puts the
|
|
7
7
|
This SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
8
8
|
|
9
9
|
- API version: 1.0.0
|
10
|
-
- Package version: 4.
|
10
|
+
- Package version: 4.2.0
|
11
11
|
- Build package: org.openapitools.codegen.languages.RubyClientCodegen
|
12
12
|
For more information, please visit [https://www.freeclimb.com/support/](https://www.freeclimb.com/support/)
|
13
13
|
|
@@ -15,7 +15,7 @@ For more information, please visit [https://www.freeclimb.com/support/](https://
|
|
15
15
|
|
16
16
|
Add this to the Gemfile:
|
17
17
|
|
18
|
-
gem 'freeclimb', '~> 4.
|
18
|
+
gem 'freeclimb', '~> 4.2.0'
|
19
19
|
|
20
20
|
and run from your terminal
|
21
21
|
|
@@ -44,9 +44,9 @@ gem build freeclimb.gemspec
|
|
44
44
|
Then either install the gem locally:
|
45
45
|
|
46
46
|
```shell
|
47
|
-
gem install ./freeclimb-4.
|
47
|
+
gem install ./freeclimb-4.2.0.gem
|
48
48
|
```
|
49
|
-
(for development, run `gem install --dev ./freeclimb-4.
|
49
|
+
(for development, run `gem install --dev ./freeclimb-4.2.0.gem` to install the development dependencies)
|
50
50
|
|
51
51
|
## Getting Started
|
52
52
|
|
@@ -285,7 +285,30 @@ Class | Method | HTTP request | Description
|
|
285
285
|
|
286
286
|
- **Type**: HTTP basic authentication
|
287
287
|
|
288
|
+
<a name="documentation-for-verify-request-signature"></a>
|
289
|
+
|
290
|
+
## Documentation for verifying request signature
|
291
|
+
|
292
|
+
- To verify the signature request, we will need to use the verifySignatureRequest method within the Request Verifier class
|
293
|
+
|
294
|
+
Freeclimb::RequestVerifier.verify_request_signature(requestBody, requestHeader, signingSecret, tolerance)
|
295
|
+
|
296
|
+
This is a method that you can call directly from the request verifier class, it will throw exceptions depending on whether all parts of the request signature is valid otherwise it will throw a specific error message depending on which request signature part is causing issues
|
297
|
+
|
298
|
+
This method requires a requestBody of type string, a requestHeader of type string, a signingSecret of type string, and a tolerance value of type int
|
299
|
+
|
300
|
+
Example code down below
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
class RequestVerifier
|
304
|
+
def verify_request_signature_example()
|
305
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
306
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
307
|
+
tolerance = (5 * 60)
|
308
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
309
|
+
Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance)
|
310
|
+
```
|
288
311
|
|
289
312
|
## Getting Help
|
290
313
|
|
291
|
-
If you are experiencing difficulties, [contact support](https://freeclimb.com/support).
|
314
|
+
If you are experiencing difficulties, [contact support](https://freeclimb.com/support).
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Freeclimb
|
2
|
+
class RequestVerifier
|
3
|
+
class << self
|
4
|
+
@@DEFAULT_TOLERANCE = 5 * 60 * 1000
|
5
|
+
|
6
|
+
def verify_request_signature(request_body, request_header, signing_secret, tolerance=DEFAULT_TOLERANCE)
|
7
|
+
request_verifier_object = Freeclimb::RequestVerifier.new()
|
8
|
+
request_verifier_object.instance_eval{ check_request_body(request_body) }
|
9
|
+
request_verifier_object.instance_eval{ check_request_header(request_header) }
|
10
|
+
request_verifier_object.instance_eval{ check_signing_secret(signing_secret) }
|
11
|
+
request_verifier_object.instance_eval{ check_tolerance(tolerance) }
|
12
|
+
info = Freeclimb::SignatureInformation.new(request_header)
|
13
|
+
request_verifier_object.instance_eval{ verify_tolerance(info, tolerance) }
|
14
|
+
request_verifier_object.instance_eval{ verify_signature(info, request_body, signing_secret)}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def check_request_body(request_body)
|
21
|
+
if request_body == "" || request_body == nil
|
22
|
+
raise 'Request Body cannot be empty or null'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_request_header(request_header)
|
27
|
+
if request_header == "" || request_header == nil
|
28
|
+
raise 'Error with request header, Request header is empty'
|
29
|
+
elsif !(request_header.include? "t")
|
30
|
+
raise 'Error with request header, timestamp is not present'
|
31
|
+
elsif !(request_header.include? "v1")
|
32
|
+
raise 'Error with request header, signatures are not present'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_signing_secret(signing_secret)
|
37
|
+
if signing_secret == "" || signing_secret == nil
|
38
|
+
raise 'Signing secret cannot be empty or null'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_tolerance(tolerance)
|
43
|
+
if tolerance <= 0 || !(tolerance.is_a? Integer)
|
44
|
+
raise 'Tolerance value must be a positive integer'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def verify_tolerance(info, tolerance)
|
49
|
+
currentTime = info.get_current_unix_time()
|
50
|
+
if !info.is_request_time_valid(tolerance)
|
51
|
+
raise "Request time exceeded tolerance threshold. Request: " + info.request_timestamp.to_s + ", CurrentTime: " + currentTime.to_s + ", tolerance: " + tolerance.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def verify_signature(info, request_body, signing_secret)
|
56
|
+
if !info.is_signature_safe(request_body, signing_secret)
|
57
|
+
raise "Unverified signature request, If this request was unexpected, it may be from a bad actor. Please proceed with caution. If the request was exepected, please check any typos or issues with the signingSecret"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module Freeclimb
|
5
|
+
class SignatureInformation
|
6
|
+
attr_accessor :request_timestamp
|
7
|
+
attr_accessor :signatures
|
8
|
+
|
9
|
+
def initialize(request_header)
|
10
|
+
@request_timestamp = 0
|
11
|
+
@signatures = []
|
12
|
+
signatureHeader = request_header.try(:split, ",")
|
13
|
+
signatureHeader.each { |signature|
|
14
|
+
header, value = signature.try(:split, "=")
|
15
|
+
if header == "t"
|
16
|
+
@request_timestamp = value.to_i
|
17
|
+
elsif header == "v1"
|
18
|
+
@signatures.append(value)
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_request_time_valid(tolerance)
|
24
|
+
currentTime = self.get_current_unix_time()
|
25
|
+
timeCalculation = @request_timestamp + tolerance
|
26
|
+
return (timeCalculation) < currentTime
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_signature_safe(request_body, signing_secret)
|
30
|
+
hashValue = self.compute_hash(request_body, signing_secret)
|
31
|
+
return @signatures.include? hashValue
|
32
|
+
end
|
33
|
+
|
34
|
+
def compute_hash(request_body, signing_secret)
|
35
|
+
data = @request_timestamp.to_s + "." + request_body
|
36
|
+
return OpenSSL::HMAC.hexdigest('sha256', signing_secret, data)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_current_unix_time()
|
40
|
+
return DateTime.now.strftime('%s').to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
private :compute_hash
|
44
|
+
end
|
45
|
+
end
|
data/lib/freeclimb/version.rb
CHANGED
data/lib/freeclimb.rb
CHANGED
@@ -148,6 +148,10 @@ require 'freeclimb/models/unpark'
|
|
148
148
|
# APIs
|
149
149
|
require 'freeclimb/api/default_api'
|
150
150
|
|
151
|
+
#Utils
|
152
|
+
require 'freeclimb/utils/signature_information'
|
153
|
+
require 'freeclimb/utils/request_verifier'
|
154
|
+
|
151
155
|
module Freeclimb
|
152
156
|
class << self
|
153
157
|
# Customize default settings for the SDK using block.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'RequestVerifier' do
|
4
|
+
before do
|
5
|
+
@request_verifier_object = Freeclimb::RequestVerifier.new()
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#check_request_body' do
|
9
|
+
context 'Request Body is empty' do
|
10
|
+
it 'throws "Request Body cannot be empty or null"' do
|
11
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
12
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
13
|
+
tolerance = 5 * 60
|
14
|
+
request_body = ""
|
15
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Request Body cannot be empty or null")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context 'Request Body is nil' do
|
19
|
+
it 'throws "Request Body cannot be empty or null"' do
|
20
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
21
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
22
|
+
tolerance = 5 * 60
|
23
|
+
request_body = nil
|
24
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Request Body cannot be empty or null")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#check_request_header' do
|
30
|
+
context 'signatures are not present' do
|
31
|
+
it 'throws "Error with request header, signatures are not present"' do
|
32
|
+
request_header = "t=1679944186,"
|
33
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
34
|
+
tolerance = 5 * 60
|
35
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
36
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Error with request header, signatures are not present")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context 'timestamp is not present' do
|
40
|
+
it 'throws "Error with request header, timestamp is not present"' do
|
41
|
+
request_header = "v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
42
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
43
|
+
tolerance = 5 * 60
|
44
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
45
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Error with request header, timestamp is not present")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'Request header is empty' do
|
49
|
+
it 'throws "Error with request header, Request header is empty"' do
|
50
|
+
request_header = ""
|
51
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
52
|
+
tolerance = 5 * 60
|
53
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
54
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Error with request header, Request header is empty")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#check_signing_secret' do
|
60
|
+
context 'Signing secret is empty' do
|
61
|
+
it 'throws "Signing secret cannot be empty or null"' do
|
62
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
63
|
+
signing_secret = ""
|
64
|
+
tolerance = 5 * 60
|
65
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
66
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Signing secret cannot be empty or null")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
context 'Signing secret is nil' do
|
70
|
+
it 'throws "Signing secret cannot be empty or null"' do
|
71
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
72
|
+
signing_secret = nil
|
73
|
+
tolerance = 5 * 60
|
74
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
75
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Signing secret cannot be empty or null")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#check_tolerance' do
|
81
|
+
context 'Tolerance value is a negative value' do
|
82
|
+
it 'throws "Tolerance value must be a positive integer"' do
|
83
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
84
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
85
|
+
tolerance = -5
|
86
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
87
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Tolerance value must be a positive integer")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
context 'Tolerance value is 0' do
|
91
|
+
it 'throws "Tolerance value must be a positive integer"' do
|
92
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
93
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
94
|
+
tolerance = 0
|
95
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
96
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Tolerance value must be a positive integer")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
context 'Tolerance value is NaN' do
|
100
|
+
it 'throws "Tolerance value must be a positive integer"' do
|
101
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
102
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
103
|
+
tolerance = Float::NAN
|
104
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
105
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Tolerance value must be a positive integer")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#verify_tolerance' do
|
111
|
+
context 'Request plus tolerance is not less than the current datetime' do
|
112
|
+
it 'throws "Request time exceeded tolerance threshold. Request: 1900871395, CurrentTime: currentTimeValue, tolerance, toleranceValue"' do
|
113
|
+
currentTime = DateTime.now.strftime('%s').to_i
|
114
|
+
request_header = "t=1900871395,v1=1d798c86e977ff734dec3a8b8d67fe8621dcc1df46ef4212e0bfe2e122b01bfd,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
115
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
116
|
+
tolerance = (5 * 60)
|
117
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
118
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Request time exceeded tolerance threshold. Request: 1900871395" + ", CurrentTime: " + currentTime.to_s + ", tolerance: " + tolerance.to_s)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#verify_signature' do
|
124
|
+
context 'Signature request is unverified, signing secret does not exist in signatures, potential typo' do
|
125
|
+
it 'throws "Unverified signature request, If this request was unexpected, it may be from a bad actor. Please proceed with caution. If the request was exepected, please check any typos or issues with the signingSecret"' do
|
126
|
+
currentTime = DateTime.now.strftime('%s').to_i
|
127
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
128
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7794"
|
129
|
+
tolerance = (5 * 60)
|
130
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
131
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.to raise_error("Unverified signature request, If this request was unexpected, it may be from a bad actor. Please proceed with caution. If the request was exepected, please check any typos or issues with the signingSecret")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#verify_request_signature' do
|
137
|
+
context 'Request is valid' do
|
138
|
+
it 'No errors are thrown' do
|
139
|
+
request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
140
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
141
|
+
tolerance = (5 * 60)
|
142
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
143
|
+
expect { Freeclimb::RequestVerifier.verify_request_signature(request_body, request_header, signing_secret, tolerance) }.not_to raise_error
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'SignatureInformation' do
|
4
|
+
before do
|
5
|
+
@request_header = "t=1679944186,v1=c3957749baf61df4b1506802579cc69a74c77a1ae21447b930e5a704f9ec4120,v1=1ba18712726898fbbe48cd862dd096a709f7ad761a5bab14bda9ac24d963a6a8"
|
6
|
+
@signature_information_object = Freeclimb::SignatureInformation.new(@request_header)
|
7
|
+
end
|
8
|
+
describe '#is_request_time_valid' do
|
9
|
+
context 'request time is within tolerance threshold' do
|
10
|
+
it 'returns true' do
|
11
|
+
tolerance = 5 * 60
|
12
|
+
expect(@signature_information_object.is_request_time_valid(tolerance)).to be true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
context 'request time is not within tolerance threshold' do
|
16
|
+
it 'returns false since it does not match condition of request time being within tolerance threshold' do
|
17
|
+
tolerance = 5 * 60 * 10000
|
18
|
+
expect(@signature_information_object.is_request_time_valid(tolerance)).to be false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
describe '#is_signature_safe' do
|
23
|
+
context 'signingSecret exists in signature array' do
|
24
|
+
it 'returns true' do
|
25
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
26
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7793"
|
27
|
+
expect(@signature_information_object.is_signature_safe(request_body, signing_secret)).to be true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'signingSecret does not exists in signature array' do
|
31
|
+
it 'returns false since it does not match condition of signingSecret being within signature array' do
|
32
|
+
request_body = "{\"accountId\":\"AC1334ffb694cd8d969f51cddf5f7c9b478546d50c\",\"callId\":\"CAccb0b00506553cda09b51c5477f672a49e0b2213\",\"callStatus\":\"ringing\",\"conferenceId\":null,\"direction\":\"inbound\",\"from\":\"+13121000109\",\"parentCallId\":null,\"queueId\":null,\"requestType\":\"inboundCall\",\"to\":\"+13121000096\"}"
|
33
|
+
signing_secret = "sigsec_ead6d3b6904196c60835d039e91b3341c77a7794"
|
34
|
+
expect(@signature_information_object.is_signature_safe(request_body, signing_secret)).to be false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freeclimb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OpenAPI-Generator
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -330,6 +330,8 @@ files:
|
|
330
330
|
- lib/freeclimb/models/update_conference_participant_request.rb
|
331
331
|
- lib/freeclimb/models/update_conference_request.rb
|
332
332
|
- lib/freeclimb/models/update_conference_request_status.rb
|
333
|
+
- lib/freeclimb/utils/request_verifier.rb
|
334
|
+
- lib/freeclimb/utils/signature_information.rb
|
333
335
|
- lib/freeclimb/version.rb
|
334
336
|
- openapi.json
|
335
337
|
- spec/api/default_api_spec.rb
|
@@ -423,6 +425,8 @@ files:
|
|
423
425
|
- spec/models/update_conference_request_status_spec.rb
|
424
426
|
- spec/quickstart_spec.rb
|
425
427
|
- spec/spec_helper.rb
|
428
|
+
- spec/utils/request_verifier_spec.rb
|
429
|
+
- spec/utils/signature_information_spec.rb
|
426
430
|
homepage: https://freeclimb.com
|
427
431
|
licenses:
|
428
432
|
- Unlicense
|
@@ -538,3 +542,5 @@ test_files:
|
|
538
542
|
- spec/models/if_machine_spec.rb
|
539
543
|
- spec/quickstart_spec.rb
|
540
544
|
- spec/spec_helper.rb
|
545
|
+
- spec/utils/signature_information_spec.rb
|
546
|
+
- spec/utils/request_verifier_spec.rb
|