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 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