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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f77b3a7bc85c35b3ecb49de7da3c9f4a931270e83b8b69b88e2c2c060e4c14e6
4
- data.tar.gz: 6b77f343b068a63cedf2ac8683b6b7d289f77b4d4aab08905c64162a2143bd0f
3
+ metadata.gz: d7826dfa439ee726cf91d308b8cd9eee895a48d53a425ac24926765525c23760
4
+ data.tar.gz: cec6eee4ac9ca13157b48a10a23f837c18f684bf3621cbe41fb89655291e493c
5
5
  SHA512:
6
- metadata.gz: e010f34f66a2f845c498171084b8be76fa3b19639c5a80f565290c992b2768edeb55f3b647243627a3fc14688d4906d76f489b61a36bcc68bd6c1990feae0ca2
7
- data.tar.gz: f587c50c621c047f405e09203bf8ba447fccc2e4b6c2c492e66211fedc457c2e624babd24ce0622058fbf1bbb814566ff9fdd1958c8554fc6b75fd32036994fd
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- freeclimb (4.1.4)
4
+ freeclimb (4.2.0)
5
5
  typhoeus (~> 1.0, >= 1.0.1)
6
6
 
7
7
  GEM
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.1.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.1.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.1.4.gem
47
+ gem install ./freeclimb-4.2.0.gem
48
48
  ```
49
- (for development, run `gem install --dev ./freeclimb-4.1.4.gem` to install the development dependencies)
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
@@ -11,5 +11,5 @@ OpenAPI Generator version: 5.4.0
11
11
  =end
12
12
 
13
13
  module Freeclimb
14
- VERSION = '4.1.4'
14
+ VERSION = '4.2.0'
15
15
  end
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.1.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-03 00:00:00.000000000 Z
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