freeclimb 4.1.4 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|