twilio-ruby 5.10.7 → 5.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,15 +28,6 @@ module Twilio
28
28
  @v1 ||= V1.new self
29
29
  end
30
30
 
31
- ##
32
- # @param [String] sid `CJxx…xx` A system-generated 34-character string that
33
- # uniquely identifies this Composition.
34
- # @return [Twilio::REST::Video::V1::CompositionInstance] if sid was passed.
35
- # @return [Twilio::REST::Video::V1::CompositionList]
36
- def compositions(sid=:unset)
37
- self.v1.compositions(sid)
38
- end
39
-
40
31
  ##
41
32
  # @return [Twilio::REST::Video::V1::CompositionSettingsInstance]
42
33
  def composition_settings
@@ -58,6 +49,15 @@ module Twilio
58
49
  self.v1.recording_settings()
59
50
  end
60
51
 
52
+ ##
53
+ # @param [String] sid `CJxx…xx` A system-generated 34-character string that
54
+ # uniquely identifies this Composition.
55
+ # @return [Twilio::REST::Video::V1::CompositionInstance] if sid was passed.
56
+ # @return [Twilio::REST::Video::V1::CompositionList]
57
+ def compositions(sid=:unset)
58
+ self.v1.compositions(sid)
59
+ end
60
+
61
61
  ##
62
62
  # @param [String] sid A system-generated 34-character string that uniquely
63
63
  # identifies this resource.
@@ -15,28 +15,13 @@ module Twilio
15
15
  def initialize(domain)
16
16
  super
17
17
  @version = 'v1'
18
- @compositions = nil
19
18
  @composition_settings = nil
20
19
  @recordings = nil
21
20
  @recording_settings = nil
21
+ @compositions = nil
22
22
  @rooms = nil
23
23
  end
24
24
 
25
- ##
26
- # @param [String] sid The Composition Sid that uniquely identifies the Composition
27
- # to fetch.
28
- # @return [Twilio::REST::Video::V1::CompositionContext] if sid was passed.
29
- # @return [Twilio::REST::Video::V1::CompositionList]
30
- def compositions(sid=:unset)
31
- if sid.nil?
32
- raise ArgumentError, 'sid cannot be nil'
33
- elsif sid == :unset
34
- @compositions ||= CompositionList.new self
35
- else
36
- CompositionContext.new(self, sid)
37
- end
38
- end
39
-
40
25
  ##
41
26
  # @return [Twilio::REST::Video::V1::CompositionSettingsContext]
42
27
  def composition_settings
@@ -64,6 +49,21 @@ module Twilio
64
49
  @recording_settings ||= RecordingSettingsContext.new self
65
50
  end
66
51
 
52
+ ##
53
+ # @param [String] sid The Composition Sid that uniquely identifies the Composition
54
+ # to fetch.
55
+ # @return [Twilio::REST::Video::V1::CompositionContext] if sid was passed.
56
+ # @return [Twilio::REST::Video::V1::CompositionList]
57
+ def compositions(sid=:unset)
58
+ if sid.nil?
59
+ raise ArgumentError, 'sid cannot be nil'
60
+ elsif sid == :unset
61
+ @compositions ||= CompositionList.new self
62
+ else
63
+ CompositionContext.new(self, sid)
64
+ end
65
+ end
66
+
67
67
  ##
68
68
  # @param [String] sid The sid
69
69
  # @return [Twilio::REST::Video::V1::RoomContext] if sid was passed.
@@ -3,17 +3,58 @@
3
3
  module Twilio
4
4
  module Security
5
5
  class RequestValidator
6
+ ##
7
+ # Initialize a Request Validator. auth_token will either be grabbed from the global Twilio object or you can
8
+ # pass it in here.
9
+ #
10
+ # @param [String] auth_token Your account auth token, used to sign requests
6
11
  def initialize(auth_token = nil)
7
12
  @auth_token = auth_token || Twilio.auth_token
8
13
  raise ArgumentError, 'Auth token is required' if @auth_token.nil?
9
14
  end
10
15
 
16
+ ##
17
+ # Validates that after hashing a request with Twilio's request-signing algorithm
18
+ # (https://www.twilio.com/docs/usage/security#validating-requests), the hash matches the signature parameter
19
+ #
20
+ # @param [String] url The url sent to your server, including any query parameters
21
+ # @param [String, Hash, #to_unsafe_h] params In most cases, this is the POST parameters as a hash. If you received
22
+ # a bodySHA256 parameter in the query string, this parameter can instead be the POST body as a string to
23
+ # validate JSON or other text-based payloads that aren't x-www-form-urlencoded.
24
+ # @param [String] signature The expected signature, from the X-Twilio-Signature header of the request
25
+ #
26
+ # @return [Boolean] whether or not the computed signature matches the signature parameter
11
27
  def validate(url, params, signature)
12
- params_hash = params_to_hash(params)
13
- expected = build_signature_for(url, params_hash)
14
- secure_compare(expected, signature)
28
+ params_hash = body_or_hash(params)
29
+ if params_hash.is_a? Enumerable
30
+ expected = build_signature_for(url, params_hash)
31
+ secure_compare(expected, signature)
32
+ else
33
+ expected_signature = build_signature_for(url, {})
34
+ body_hash = URI.decode_www_form(URI(url).query).to_h['bodySHA256']
35
+ expected_hash = build_hash_for(params)
36
+ secure_compare(expected_signature, signature) && secure_compare(expected_hash, body_hash)
37
+ end
15
38
  end
16
39
 
40
+ ##
41
+ # Build a SHA256 hash for a body string
42
+ #
43
+ # @param [String] body String to hash
44
+ #
45
+ # @return [String] A base64-encoded SHA256 of the body string
46
+ def build_hash_for(body)
47
+ hasher = OpenSSL::Digest.new('sha256')
48
+ Base64.encode64(hasher.digest(body)).strip
49
+ end
50
+
51
+ ##
52
+ # Build a SHA1-HMAC signature for a url and parameter hash
53
+ #
54
+ # @param [String] url The request url, including any query parameters
55
+ # @param [#join] params The POST parameters
56
+ #
57
+ # @return [String] A base64 encoded SHA1-HMAC
17
58
  def build_signature_for(url, params)
18
59
  data = url + params.sort.join
19
60
  digest = OpenSSL::Digest.new('sha1')
@@ -44,11 +85,11 @@ module Twilio
44
85
  #
45
86
  # We use `to_unsafe_h` as `to_h` returns a hash of the permitted
46
87
  # parameters only and we need all the parameters to create the signature.
47
- def params_to_hash(params)
48
- if params.respond_to?(:to_unsafe_h)
49
- params.to_unsafe_h
88
+ def body_or_hash(params_or_body)
89
+ if params_or_body.respond_to?(:to_unsafe_h)
90
+ params_or_body.to_unsafe_h
50
91
  else
51
- params
92
+ params_or_body
52
93
  end
53
94
  end
54
95
  end
@@ -175,8 +175,11 @@ module Twilio
175
175
  # loop:: Times to loop message
176
176
  # language:: Message langauge
177
177
  # keyword_args:: additional attributes
178
- def say(message, voice: nil, loop: nil, language: nil, **keyword_args)
179
- append(Say.new(message, voice: voice, loop: loop, language: language, **keyword_args))
178
+ def say(message: nil, voice: nil, loop: nil, language: nil, **keyword_args)
179
+ say = Say.new(message: message, voice: voice, loop: loop, language: language, **keyword_args)
180
+
181
+ yield(say) if block_given?
182
+ append(say)
180
183
  end
181
184
 
182
185
  ##
@@ -207,10 +210,192 @@ module Twilio
207
210
  ##
208
211
  # <Say> TwiML Verb
209
212
  class Say < TwiML
210
- def initialize(message, **keyword_args)
213
+ def initialize(message: nil, **keyword_args)
211
214
  super(**keyword_args)
212
215
  @name = 'Say'
213
- @value = message
216
+ @value = message unless message.nil?
217
+ yield(self) if block_given?
218
+ end
219
+
220
+ ##
221
+ # Create a new <Break> element
222
+ # strength:: Set a pause based on strength
223
+ # time:: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms
224
+ # keyword_args:: additional attributes
225
+ def break(strength: nil, time: nil, **keyword_args)
226
+ append(SsmlBreak.new(strength: strength, time: time, **keyword_args))
227
+ end
228
+
229
+ ##
230
+ # Create a new <Emphasis> element
231
+ # words:: Words to emphasize
232
+ # level:: Specify the degree of emphasis
233
+ # keyword_args:: additional attributes
234
+ def emphasis(words, level: nil, **keyword_args)
235
+ append(SsmlEmphasis.new(words, level: level, **keyword_args))
236
+ end
237
+
238
+ ##
239
+ # Create a new <P> element
240
+ # words:: Words to speak
241
+ # keyword_args:: additional attributes
242
+ def p(words, **keyword_args)
243
+ append(SsmlP.new(words, **keyword_args))
244
+ end
245
+
246
+ ##
247
+ # Create a new <Phoneme> element
248
+ # words:: Words to speak
249
+ # alphabet:: Specify the phonetic alphabet
250
+ # ph:: Specifiy the phonetic symbols for pronunciation
251
+ # keyword_args:: additional attributes
252
+ def phoneme(words, alphabet: nil, ph: nil, **keyword_args)
253
+ append(SsmlPhoneme.new(words, alphabet: alphabet, ph: ph, **keyword_args))
254
+ end
255
+
256
+ ##
257
+ # Create a new <Prosody> element
258
+ # words:: Words to speak
259
+ # volume:: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB
260
+ # rate:: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n%
261
+ # pitch:: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n%
262
+ # keyword_args:: additional attributes
263
+ def prosody(words, volume: nil, rate: nil, pitch: nil, **keyword_args)
264
+ append(SsmlProsody.new(words, volume: volume, rate: rate, pitch: pitch, **keyword_args))
265
+ end
266
+
267
+ ##
268
+ # Create a new <S> element
269
+ # words:: Words to speak
270
+ # keyword_args:: additional attributes
271
+ def s(words, **keyword_args)
272
+ append(SsmlS.new(words, **keyword_args))
273
+ end
274
+
275
+ ##
276
+ # Create a new <Say-As> element
277
+ # words:: Words to be interpreted
278
+ # interpret-as:: Specify the type of words are spoken
279
+ # role:: Specify the format of the date when interpret-as is set to date
280
+ # keyword_args:: additional attributes
281
+ def say_as(words, interpretAs: nil, role: nil, **keyword_args)
282
+ append(SsmlSayAs.new(words, interpretAs: interpretAs, role: role, **keyword_args))
283
+ end
284
+
285
+ ##
286
+ # Create a new <Sub> element
287
+ # words:: Words to be substituted
288
+ # alias:: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation
289
+ # keyword_args:: additional attributes
290
+ def sub(words, aliasAttribute: nil, **keyword_args)
291
+ append(SsmlSub.new(words, aliasAttribute: aliasAttribute, **keyword_args))
292
+ end
293
+
294
+ ##
295
+ # Create a new <W> element
296
+ # words:: Words to speak
297
+ # role:: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning
298
+ # keyword_args:: additional attributes
299
+ def w(words, role: nil, **keyword_args)
300
+ append(SsmlW.new(words, role: role, **keyword_args))
301
+ end
302
+ end
303
+
304
+ ##
305
+ # Improving Pronunciation by Specifying Parts of Speech in <Say>
306
+ class SsmlW < TwiML
307
+ def initialize(words, **keyword_args)
308
+ super(**keyword_args)
309
+ @name = 'W'
310
+ @value = words
311
+ yield(self) if block_given?
312
+ end
313
+ end
314
+
315
+ ##
316
+ # Pronouncing Acronyms and Abbreviations in <Say>
317
+ class SsmlSub < TwiML
318
+ def initialize(words, **keyword_args)
319
+ super(**keyword_args)
320
+ @name = 'Sub'
321
+ @value = words
322
+ yield(self) if block_given?
323
+ end
324
+ end
325
+
326
+ ##
327
+ # Controlling How Special Types of Words Are Spoken in <Say>
328
+ class SsmlSayAs < TwiML
329
+ def initialize(words, **keyword_args)
330
+ super(**keyword_args)
331
+ @name = 'Say-As'
332
+ @value = words
333
+ yield(self) if block_given?
334
+ end
335
+ end
336
+
337
+ ##
338
+ # Adding A Pause Between Sentences in <Say>
339
+ class SsmlS < TwiML
340
+ def initialize(words, **keyword_args)
341
+ super(**keyword_args)
342
+ @name = 'S'
343
+ @value = words
344
+ yield(self) if block_given?
345
+ end
346
+ end
347
+
348
+ ##
349
+ # Controling Volume, Speaking Rate, and Pitch in <Say>
350
+ class SsmlProsody < TwiML
351
+ def initialize(words, **keyword_args)
352
+ super(**keyword_args)
353
+ @name = 'Prosody'
354
+ @value = words
355
+ yield(self) if block_given?
356
+ end
357
+ end
358
+
359
+ ##
360
+ # Using Phonetic Pronunciation in <Say>
361
+ class SsmlPhoneme < TwiML
362
+ def initialize(words, **keyword_args)
363
+ super(**keyword_args)
364
+ @name = 'Phoneme'
365
+ @value = words
366
+ yield(self) if block_given?
367
+ end
368
+ end
369
+
370
+ ##
371
+ # Adding a Pause Between Paragraphs in <Say>
372
+ class SsmlP < TwiML
373
+ def initialize(words, **keyword_args)
374
+ super(**keyword_args)
375
+ @name = 'P'
376
+ @value = words
377
+ yield(self) if block_given?
378
+ end
379
+ end
380
+
381
+ ##
382
+ # Emphasizing Words in <Say>
383
+ class SsmlEmphasis < TwiML
384
+ def initialize(words, **keyword_args)
385
+ super(**keyword_args)
386
+ @name = 'Emphasis'
387
+ @value = words
388
+ yield(self) if block_given?
389
+ end
390
+ end
391
+
392
+ ##
393
+ # Adding a Pause in <Say>
394
+ class SsmlBreak < TwiML
395
+ def initialize(**keyword_args)
396
+ super(**keyword_args)
397
+ @name = 'Break'
398
+
214
399
  yield(self) if block_given?
215
400
  end
216
401
  end
@@ -320,8 +505,11 @@ module Twilio
320
505
  # loop:: Times to loop message
321
506
  # language:: Message langauge
322
507
  # keyword_args:: additional attributes
323
- def say(message, voice: nil, loop: nil, language: nil, **keyword_args)
324
- append(Say.new(message, voice: voice, loop: loop, language: language, **keyword_args))
508
+ def say(message: nil, voice: nil, loop: nil, language: nil, **keyword_args)
509
+ say = Say.new(message: message, voice: voice, loop: loop, language: language, **keyword_args)
510
+
511
+ yield(say) if block_given?
512
+ append(say)
325
513
  end
326
514
 
327
515
  ##
@@ -1,3 +1,3 @@
1
1
  module Twilio
2
- VERSION = '5.10.7'
2
+ VERSION = '5.11.0'
3
3
  end
@@ -41,7 +41,7 @@ describe 'IpAddress' do
41
41
  "date_updated": "Mon, 20 Jul 2015 17:27:10 +0000",
42
42
  "friendly_name": "friendly_name",
43
43
  "ip_access_control_list_sid": "ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
44
- "ip_address": "ip_address",
44
+ "ip_address": "192.168.1.1",
45
45
  "cidr_prefix_length": 32,
46
46
  "sid": "IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
47
47
  "uri": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SIP/IpAccessControlLists/ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/IpAddresses/IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json"
@@ -120,7 +120,7 @@ describe 'IpAddress' do
120
120
  "date_updated": "Mon, 20 Jul 2015 17:27:10 +0000",
121
121
  "friendly_name": "friendly_name",
122
122
  "ip_access_control_list_sid": "ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
123
- "ip_address": "ip_address",
123
+ "ip_address": "192.168.1.1",
124
124
  "cidr_prefix_length": 32,
125
125
  "sid": "IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
126
126
  "uri": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SIP/IpAccessControlLists/ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/IpAddresses/IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json"
@@ -164,7 +164,7 @@ describe 'IpAddress' do
164
164
  "date_updated": "Mon, 20 Jul 2015 17:27:10 +0000",
165
165
  "friendly_name": "friendly_name",
166
166
  "ip_access_control_list_sid": "ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
167
- "ip_address": "ip_address",
167
+ "ip_address": "192.168.1.1",
168
168
  "cidr_prefix_length": 32,
169
169
  "sid": "IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
170
170
  "uri": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SIP/IpAccessControlLists/ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/IpAddresses/IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json"
@@ -208,7 +208,7 @@ describe 'IpAddress' do
208
208
  "date_updated": "Mon, 20 Jul 2015 17:27:10 +0000",
209
209
  "friendly_name": "friendly_name",
210
210
  "ip_access_control_list_sid": "ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
211
- "ip_address": "ip_address",
211
+ "ip_address": "192.168.1.1",
212
212
  "cidr_prefix_length": 32,
213
213
  "sid": "IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
214
214
  "uri": "/2010-04-01/Accounts/ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SIP/IpAccessControlLists/ALaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/IpAddresses/IPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json"
@@ -30,84 +30,57 @@ describe Twilio::Security::RequestValidator do
30
30
  end
31
31
 
32
32
  describe 'validations' do
33
- let(:token) { '2bd9e9638872de601313dc77410d3b23' }
33
+ let(:token) { '12345' }
34
34
 
35
35
  let(:validator) { Twilio::Security::RequestValidator.new token }
36
36
 
37
- let(:voice_url) { 'http://twiliotests.heroku.com/validate/voice' }
37
+ let(:url) { 'https://mycompany.com/myapp.php?foo=1&bar=2' }
38
38
 
39
- let(:sms_url) { 'http://twiliotests.heroku.com/validate/sms' }
39
+ let(:default_signature) { 'RSOYDt4T1cUTdK1PDd93/VVr8B8=' }
40
40
 
41
- let(:voice_params) do
41
+ let(:body) { '{"property": "value", "boolean": true}' }
42
+
43
+ let(:body_hash) { 'Ch/3Y02as7ldtcmi3+lBbkFQKyg6gMfPGWMmMvluZiA=' }
44
+
45
+ let(:body_hash_encoded) { body_hash.gsub('+', '%2B').gsub('=', '%3D') }
46
+
47
+ let(:params) do
42
48
  {
43
- 'ToState' => 'California',
44
- 'CalledState' => 'California',
45
- 'Direction' => 'inbound',
46
- 'FromState' => 'CA',
47
- 'AccountSid' => 'ACba8bc05eacf94afdae398e642c9cc32d',
48
- 'Caller' => '+14153595711',
49
- 'CallerZip' => '94108',
50
- 'CallerCountry' => 'US',
51
- 'From' => '+14153595711',
52
- 'FromCity' => 'SAN FRANCISCO',
53
- 'CallerCity' => 'SAN FRANCISCO',
54
- 'To' => '+14157669926',
55
- 'FromZip' => '94108',
56
- 'FromCountry' => 'US',
57
- 'ToCity' => '',
58
- 'CallStatus' => 'ringing',
59
- 'CalledCity' => '',
60
- 'CallerState' => 'CA',
61
- 'CalledZip' => '',
62
- 'ToZip' => '',
63
- 'ToCountry' => 'US',
64
- 'CallSid' => 'CA136d09cd59a3c0ec8dbff44da5c03f31',
65
- 'CalledCountry' => 'US',
66
- 'Called' => '+14157669926',
67
- 'ApiVersion' => '2010-04-01',
68
- 'ApplicationSid' => 'AP44efecad51364e80b133bb7c07eb8204'
49
+ 'From' => '+14158675309',
50
+ 'To' => '+18005551212',
51
+ 'CallSid' => 'CA1234567890ABCDE',
52
+ 'Caller' => '+14158675309',
53
+ 'Digits' => '1234'
69
54
  }
70
55
  end
71
56
 
72
- let(:sms_params) do
73
- {
74
- 'ToState' => 'CA',
75
- 'FromState' => 'CA',
76
- 'AccountSid' => 'ACba8bc05eacf94afdae398e642c9cc32d',
77
- 'SmsMessageSid' => 'SM2003cbd5e6a3701999aa3e5f20ff2787',
78
- 'Body' => 'Orly',
79
- 'From' => '+14159354345',
80
- 'FromCity' => 'SAN FRANCISCO',
81
- 'SmsStatus' => 'received',
82
- 'FromZip' => '94107',
83
- 'FromCountry' => 'US',
84
- 'To' => '+14158141819',
85
- 'ToCity' => 'SAN FRANCISCO',
86
- 'ToZip' => '94105',
87
- 'ToCountry' => 'US',
88
- 'ApiVersion' => '2010-04-01',
89
- 'SmsSid' => 'SM2003cbd5e6a3701999aa3e5f20ff2787'
90
- }
57
+ it 'should build a correct signature' do
58
+ expect(validator.build_signature_for(url, params)).to eq(default_signature)
59
+ end
60
+ it 'should validate an authentic Twilio request' do
61
+ expect(validator.validate(url, params, default_signature)).to eq(true)
62
+ end
63
+
64
+ it 'should not validate a request with wrong signature' do
65
+ expect(validator.validate(url, params, 'fake_signature')).to eq(false)
91
66
  end
92
67
 
93
- it 'should validate an authentic Twilio Voice request' do
94
- signature = 'oVb2kXoVy8GEfwBDjR8bk/ZZ6eA='
95
- expect(validator.validate(voice_url, voice_params, signature)).to eq(true)
68
+ it 'should hash body correctly' do
69
+ expect(validator.build_hash_for(body)).to eq(body_hash)
96
70
  end
97
71
 
98
- it 'should validate an authentic Twilio SMS request' do
99
- signature = 'mxeiv65lEe0b8L6LdVw2jgJi8yw='
100
- expect(validator.validate(sms_url, sms_params, signature)).to eq(true)
72
+ it 'should validate requests with body correctly' do
73
+ url_with_hash = "#{url}&bodySHA256=#{body_hash_encoded}"
74
+ expect(validator.validate(url_with_hash, body, 'afcFvPLPYT8mg/JyIVkdnqQKa2s=')).to eq(true)
101
75
  end
102
76
 
103
- it 'should not validate a Twilio Voice request with wrong signature' do
104
- signature = 'foo'
105
- expect(validator.validate(voice_url, voice_params, signature)).to eq(false)
77
+ it 'should validate with no other GET parameters' do
78
+ url_with_hash = "https://mycompany.com/myapp.php?bodySHA256=#{body_hash_encoded}"
79
+ expect(validator.validate(url_with_hash, body, 'DXnNFCj8DJ/hZmiSg4UzaDHw5Og=')).to eq(true)
106
80
  end
107
81
 
108
- it 'should not validate a Twilio SMS request with wrong signature' do
109
- signature = 'bar'
110
- expect(validator.validate(sms_url, sms_params, signature)).to eq(false)
82
+ it 'should fail validation with body but no signature' do
83
+ expect(validator.validate(url, body, default_signature)).to eq(false)
111
84
  end
112
85
  end
113
86
  end