restcomm-ruby 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS.md +38 -0
- data/CHANGES.md +171 -0
- data/Gemfile +11 -0
- data/LICENSE +662 -0
- data/LICENSE.md +19 -0
- data/Makefile +12 -0
- data/README.md +155 -0
- data/Rakefile +10 -0
- data/conf/cacert.pem +3376 -0
- data/docs/Makefile +130 -0
- data/docs/_themes/LICENSE +45 -0
- data/docs/_themes/README.rst +25 -0
- data/docs/_themes/flask_theme_support.py +86 -0
- data/docs/_themes/kr/layout.html +32 -0
- data/docs/_themes/kr/relations.html +19 -0
- data/docs/_themes/kr/static/flasky.css_t +469 -0
- data/docs/_themes/kr/static/small_flask.css +70 -0
- data/docs/_themes/kr/theme.conf +7 -0
- data/docs/_themes/kr_small/layout.html +22 -0
- data/docs/_themes/kr_small/static/flasky.css_t +287 -0
- data/docs/_themes/kr_small/theme.conf +10 -0
- data/docs/changelog.rst +1 -0
- data/docs/conf.py +266 -0
- data/docs/faq.rst +42 -0
- data/docs/getting-started.rst +100 -0
- data/docs/index.rst +109 -0
- data/docs/make.bat +170 -0
- data/docs/src/pip-delete-this-directory.txt +5 -0
- data/docs/usage/accounts.rst +96 -0
- data/docs/usage/addresses.rst +102 -0
- data/docs/usage/applications.rst +111 -0
- data/docs/usage/basics.rst +120 -0
- data/docs/usage/caller-ids.rst +47 -0
- data/docs/usage/conferences.rst +112 -0
- data/docs/usage/errors.rst +31 -0
- data/docs/usage/messages.rst +142 -0
- data/docs/usage/notifications.rst +72 -0
- data/docs/usage/phone-calls.rst +193 -0
- data/docs/usage/phone-numbers.rst +192 -0
- data/docs/usage/queues.rst +117 -0
- data/docs/usage/recordings.rst +102 -0
- data/docs/usage/sip.rst +108 -0
- data/docs/usage/token-generation.rst +96 -0
- data/docs/usage/transcriptions.rst +34 -0
- data/docs/usage/twiml.rst +69 -0
- data/docs/usage/validation.rst +107 -0
- data/examples/examples.rb +200 -0
- data/examples/print-call-log.rb +25 -0
- data/lib/rack/restcomm_webhook_authentication.rb +47 -0
- data/lib/restcomm-ruby.rb +103 -0
- data/lib/restcomm-ruby/rest/accounts.rb +17 -0
- data/lib/restcomm-ruby/rest/addresses.rb +12 -0
- data/lib/restcomm-ruby/rest/addresses/dependent_phone_numbers.rb +6 -0
- data/lib/restcomm-ruby/rest/applications.rb +6 -0
- data/lib/restcomm-ruby/rest/authorized_connect_apps.rb +6 -0
- data/lib/restcomm-ruby/rest/available_phone_numbers.rb +13 -0
- data/lib/restcomm-ruby/rest/available_phone_numbers/country.rb +10 -0
- data/lib/restcomm-ruby/rest/available_phone_numbers/local.rb +11 -0
- data/lib/restcomm-ruby/rest/available_phone_numbers/mobile.rb +11 -0
- data/lib/restcomm-ruby/rest/available_phone_numbers/toll_free.rb +11 -0
- data/lib/restcomm-ruby/rest/call_feedback.rb +28 -0
- data/lib/restcomm-ruby/rest/call_feedback_summary.rb +13 -0
- data/lib/restcomm-ruby/rest/calls.rb +37 -0
- data/lib/restcomm-ruby/rest/client.rb +555 -0
- data/lib/restcomm-ruby/rest/conferences.rb +12 -0
- data/lib/restcomm-ruby/rest/conferences/participants.rb +23 -0
- data/lib/restcomm-ruby/rest/connect_apps.rb +6 -0
- data/lib/restcomm-ruby/rest/errors.rb +14 -0
- data/lib/restcomm-ruby/rest/incoming_phone_numbers.rb +17 -0
- data/lib/restcomm-ruby/rest/incoming_phone_numbers/local.rb +13 -0
- data/lib/restcomm-ruby/rest/incoming_phone_numbers/mobile.rb +13 -0
- data/lib/restcomm-ruby/rest/incoming_phone_numbers/toll_free.rb +13 -0
- data/lib/restcomm-ruby/rest/instance_resource.rb +88 -0
- data/lib/restcomm-ruby/rest/list_resource.rb +132 -0
- data/lib/restcomm-ruby/rest/media.rb +14 -0
- data/lib/restcomm-ruby/rest/messages.rb +23 -0
- data/lib/restcomm-ruby/rest/next_gen_list_resource.rb +29 -0
- data/lib/restcomm-ruby/rest/notifications.rb +6 -0
- data/lib/restcomm-ruby/rest/outgoing_caller_ids.rb +25 -0
- data/lib/restcomm-ruby/rest/queues.rb +12 -0
- data/lib/restcomm-ruby/rest/queues/members.rb +29 -0
- data/lib/restcomm-ruby/rest/recordings.rb +35 -0
- data/lib/restcomm-ruby/rest/sandbox.rb +5 -0
- data/lib/restcomm-ruby/rest/sip.rb +10 -0
- data/lib/restcomm-ruby/rest/sip/credential_lists.rb +11 -0
- data/lib/restcomm-ruby/rest/sip/credential_lists/credentials.rb +6 -0
- data/lib/restcomm-ruby/rest/sip/domains.rb +12 -0
- data/lib/restcomm-ruby/rest/sip/domains/credential_list_mappings.rb +6 -0
- data/lib/restcomm-ruby/rest/sip/domains/ip_access_control_list_mappings.rb +6 -0
- data/lib/restcomm-ruby/rest/sip/ip_access_control_lists.rb +11 -0
- data/lib/restcomm-ruby/rest/sip/ip_access_control_lists/ip_addresses.rb +6 -0
- data/lib/restcomm-ruby/rest/sms.rb +11 -0
- data/lib/restcomm-ruby/rest/sms/messages.rb +39 -0
- data/lib/restcomm-ruby/rest/sms/short_codes.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/activities.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/events.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/reservations.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/task_queues.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/task_queues_statistics.rb +15 -0
- data/lib/restcomm-ruby/rest/task_router/tasks.rb +15 -0
- data/lib/restcomm-ruby/rest/task_router/workers.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/workers_statistics.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/workflow_statistics.rb +7 -0
- data/lib/restcomm-ruby/rest/task_router/workflows.rb +8 -0
- data/lib/restcomm-ruby/rest/task_router/workspace_statistics.rb +7 -0
- data/lib/restcomm-ruby/rest/task_router/workspaces.rb +15 -0
- data/lib/restcomm-ruby/rest/tokens.rb +7 -0
- data/lib/restcomm-ruby/rest/transcriptions.rb +6 -0
- data/lib/restcomm-ruby/rest/usage.rb +10 -0
- data/lib/restcomm-ruby/rest/usage/records.rb +21 -0
- data/lib/restcomm-ruby/rest/usage/triggers.rb +12 -0
- data/lib/restcomm-ruby/rest/utils.rb +49 -0
- data/lib/restcomm-ruby/task_router.rb +0 -0
- data/lib/restcomm-ruby/task_router/capability.rb +87 -0
- data/lib/restcomm-ruby/twiml/response.rb +16 -0
- data/lib/restcomm-ruby/util.rb +15 -0
- data/lib/restcomm-ruby/util/capability.rb +64 -0
- data/lib/restcomm-ruby/util/configuration.rb +7 -0
- data/lib/restcomm-ruby/util/request_validator.rb +37 -0
- data/lib/restcomm-ruby/version.rb +3 -0
- data/restcomm-ruby.gemspec +34 -0
- data/spec/rack/twilio_webhook_authentication_spec.rb +110 -0
- data/spec/rest/account_spec.rb +89 -0
- data/spec/rest/address_spec.rb +11 -0
- data/spec/rest/call_feedback_spec.rb +12 -0
- data/spec/rest/call_feedback_summary_spec.rb +9 -0
- data/spec/rest/call_spec.rb +22 -0
- data/spec/rest/client_spec.rb +258 -0
- data/spec/rest/conference_spec.rb +11 -0
- data/spec/rest/instance_resource_spec.rb +15 -0
- data/spec/rest/message_spec.rb +12 -0
- data/spec/rest/numbers_spec.rb +58 -0
- data/spec/rest/queue_spec.rb +11 -0
- data/spec/rest/recording_spec.rb +11 -0
- data/spec/rest/sms/message_spec.rb +37 -0
- data/spec/rest/sms/messages_spec.rb +36 -0
- data/spec/rest/task_router/reservation_spec.rb +9 -0
- data/spec/rest/task_router/task_queue_spec.rb +9 -0
- data/spec/rest/token_spec.rb +7 -0
- data/spec/rest/utils_spec.rb +45 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/fakeweb.rb +2 -0
- data/spec/task_router_spec.rb +114 -0
- data/spec/twilio_spec.rb +15 -0
- data/spec/util/capability_spec.rb +186 -0
- data/spec/util/configuration_spec.rb +13 -0
- data/spec/util/request_validator_spec.rb +93 -0
- data/spec/util/url_encode_spec.rb +12 -0
- metadata +298 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Restcomm
|
2
|
+
module REST
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
def restify(something)
|
6
|
+
return key_map(something, :restify) if something.is_a? Hash
|
7
|
+
string = something.to_s
|
8
|
+
string.split('_').map do |string_part|
|
9
|
+
string_part[0,1].capitalize + string_part[1..-1]
|
10
|
+
end.join
|
11
|
+
end
|
12
|
+
|
13
|
+
def derestify(something)
|
14
|
+
return key_map(something, :derestify) if something.is_a? Hash
|
15
|
+
string = something.to_s
|
16
|
+
string = string[0,1].downcase + string[1..-1]
|
17
|
+
string.gsub(/[A-Z][a-z]*/) { |s| "_#{s.downcase}" }
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def resource(*resources)
|
23
|
+
custom_resource_names = { sms: 'SMS', sip: 'SIP' }
|
24
|
+
resources.each do |r|
|
25
|
+
resource = restify r
|
26
|
+
relative_path = custom_resource_names.fetch(r, resource)
|
27
|
+
path = "#{@path}/#{relative_path}"
|
28
|
+
enclosing_module = if @submodule == nil
|
29
|
+
Restcomm::REST
|
30
|
+
else
|
31
|
+
Restcomm::REST.const_get(@submodule)
|
32
|
+
end
|
33
|
+
resource_class = enclosing_module.const_get resource
|
34
|
+
instance_variable_set("@#{r}", resource_class.new(path, @client))
|
35
|
+
end
|
36
|
+
self.class.instance_eval { attr_reader *resources }
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def key_map(something, method)
|
42
|
+
something = something.to_a.flat_map do |pair|
|
43
|
+
[send(method, pair[0]).to_sym, pair[1]]
|
44
|
+
end
|
45
|
+
Hash[*something]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
File without changes
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Restcomm
|
2
|
+
module TaskRouter
|
3
|
+
class Capability
|
4
|
+
|
5
|
+
TASK_ROUTER_BASE_URL = 'https://taskrouter.restcomm.com'
|
6
|
+
TASK_ROUTER_VERSION = 'v1'
|
7
|
+
TASK_ROUTER_WEBSOCKET_BASE_URL = 'https://event-bridge.restcomm.com/v1/wschannels'
|
8
|
+
|
9
|
+
REQUIRED = {required: true}
|
10
|
+
OPTIONAL = {required: false}
|
11
|
+
|
12
|
+
def initialize(account_sid, auth_token, workspace_sid, worker_sid)
|
13
|
+
@account_sid = account_sid
|
14
|
+
@auth_token = auth_token
|
15
|
+
@workspace_sid = workspace_sid
|
16
|
+
@worker_sid = worker_sid
|
17
|
+
@policies = []
|
18
|
+
allow_websocket_requests
|
19
|
+
allow_activity_list_fetch
|
20
|
+
end
|
21
|
+
|
22
|
+
def workspace_url
|
23
|
+
"#{TASK_ROUTER_BASE_URL}/#{TASK_ROUTER_VERSION}/Workspaces/#{@workspace_sid}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def worker_url
|
27
|
+
"#{workspace_url}/Workers/#{@worker_sid}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def allow_worker_activity_updates
|
31
|
+
add_policy(worker_url, "POST", nil, {ActivitySid: REQUIRED})
|
32
|
+
end
|
33
|
+
|
34
|
+
def allow_worker_fetch_attributes
|
35
|
+
add_policy(worker_url, "GET")
|
36
|
+
end
|
37
|
+
|
38
|
+
def allow_task_reservation_updates
|
39
|
+
task_url = "#{workspace_url}/Tasks/**"
|
40
|
+
add_policy(task_url, "POST", nil, {ReservationStatus: REQUIRED})
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_policy(url, method, query_filters = nil, post_filters = nil, allowed = true)
|
44
|
+
policy = {
|
45
|
+
url: url,
|
46
|
+
method: method,
|
47
|
+
query_filter: query_filters || {},
|
48
|
+
post_filter: post_filters || {},
|
49
|
+
allow: allowed
|
50
|
+
}
|
51
|
+
|
52
|
+
@policies.push(policy)
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_token(ttl = 3600)
|
56
|
+
payload = {
|
57
|
+
iss: @account_sid,
|
58
|
+
exp: (Time.now.to_i + ttl),
|
59
|
+
version: @version,
|
60
|
+
friendly_name: @worker_sid,
|
61
|
+
policies: @policies,
|
62
|
+
account_sid: @account_sid,
|
63
|
+
worker_sid: @worker_sid,
|
64
|
+
channel: @worker_sid,
|
65
|
+
workspace_sid: @workspace_sid
|
66
|
+
}
|
67
|
+
|
68
|
+
JWT.encode payload, @auth_token
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def allow_websocket_requests
|
74
|
+
worker_url = "#{TASK_ROUTER_WEBSOCKET_BASE_URL}/#{@account_sid}/#{@worker_sid}"
|
75
|
+
['GET', 'POST'].each do |meth|
|
76
|
+
add_policy(worker_url, meth)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def allow_activity_list_fetch
|
81
|
+
url = "#{workspace_url}/Activities"
|
82
|
+
add_policy(url, 'GET')
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Restcomm
|
2
|
+
module Util
|
3
|
+
def url_encode(hash)
|
4
|
+
hash.to_a.map {|p| p.map {|e| CGI.escape get_string(e)}.join '='}.join '&'
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_string(obj)
|
8
|
+
if obj.respond_to?(:strftime)
|
9
|
+
obj.strftime('%Y-%m-%d')
|
10
|
+
else
|
11
|
+
obj.to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Restcomm
|
2
|
+
module Util
|
3
|
+
class Capability
|
4
|
+
|
5
|
+
include Restcomm::Util
|
6
|
+
|
7
|
+
def initialize(account_sid = nil, auth_token = nil)
|
8
|
+
@account_sid = account_sid || Restcomm.account_sid
|
9
|
+
@auth_token = auth_token || Restcomm.auth_token
|
10
|
+
if @account_sid.nil? || @auth_token.nil?
|
11
|
+
raise ArgumentError, 'Account SID and auth token are required'
|
12
|
+
end
|
13
|
+
@capabilities = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def allow_client_incoming(client_name)
|
17
|
+
@client_name = client_name # stash for use in outgoing
|
18
|
+
scope_params = { 'clientName' => client_name }
|
19
|
+
@capabilities << scope_uri_for('client', 'incoming', scope_params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def allow_client_outgoing(app_sid, params = {})
|
23
|
+
@allow_client_outgoing = true
|
24
|
+
@outgoing_scope_params = { 'appSid' => app_sid }
|
25
|
+
unless params.empty?
|
26
|
+
@outgoing_scope_params['appParams'] = url_encode params
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def allow_event_stream(filters = {})
|
31
|
+
scope_params = { 'path' => '/2010-04-01/Events' }
|
32
|
+
scope_params['params'] = filters unless filters.empty?
|
33
|
+
@capabilities << scope_uri_for('stream', 'subscribe', scope_params)
|
34
|
+
end
|
35
|
+
|
36
|
+
def scope_uri_for(service, privilege, params = {})
|
37
|
+
scope_uri = "scope:#{service}:#{privilege}"
|
38
|
+
scope_uri << "?#{url_encode(params)}" unless params.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate(ttl = 3600)
|
42
|
+
|
43
|
+
capabilities = @capabilities.clone # we need a local copy to work on
|
44
|
+
|
45
|
+
# build the outgoing scope lazily so that we can use @client_name
|
46
|
+
if @allow_client_outgoing
|
47
|
+
params = @outgoing_scope_params
|
48
|
+
params.merge!('clientName' => @client_name) if @client_name
|
49
|
+
capabilities << scope_uri_for('client', 'outgoing', params)
|
50
|
+
end
|
51
|
+
|
52
|
+
payload = {
|
53
|
+
'scope' => capabilities.join(' '),
|
54
|
+
'iss' => @account_sid,
|
55
|
+
'exp' => (Time.now.to_i + ttl),
|
56
|
+
}
|
57
|
+
|
58
|
+
JWT.encode payload, @auth_token
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Restcomm
|
2
|
+
module Util
|
3
|
+
class RequestValidator
|
4
|
+
|
5
|
+
def initialize(auth_token = nil)
|
6
|
+
@auth_token = auth_token || Restcomm.auth_token
|
7
|
+
raise ArgumentError, 'Auth token is required' if @auth_token.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate(url, params, signature)
|
11
|
+
expected = build_signature_for url, params
|
12
|
+
secure_compare(expected, signature)
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_signature_for(url, params)
|
16
|
+
data = url + params.sort.join
|
17
|
+
digest = OpenSSL::Digest.new('sha1')
|
18
|
+
Base64.encode64(OpenSSL::HMAC.digest(digest, @auth_token, data)).strip
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Compares two strings in constant time to avoid timing attacks.
|
24
|
+
# Borrowed from ActiveSupport::MessageVerifier.
|
25
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb
|
26
|
+
def secure_compare(a, b)
|
27
|
+
return false unless a.bytesize == b.bytesize
|
28
|
+
|
29
|
+
l = a.unpack("C#{a.bytesize}")
|
30
|
+
|
31
|
+
res = 0
|
32
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
33
|
+
res == 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'restcomm-ruby/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'restcomm-ruby'
|
8
|
+
spec.version = Restcomm::VERSION
|
9
|
+
spec.authors = ['Andrew Benton']
|
10
|
+
spec.email = ['andrew@twilio.com']
|
11
|
+
spec.summary = 'A simple library for communicating with the Restcomm REST API, building RCML, and generating Restcomm Client Capability Tokens'
|
12
|
+
spec.description = 'A simple library for communicating with the Restcomm REST API, building RCML, and generating Restcomm Client Capability Tokens'
|
13
|
+
spec.homepage = 'http://github.com/restcomm/restcomm-ruby'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
spec.required_ruby_version = '>= 1.9.3'
|
21
|
+
spec.extra_rdoc_files = ['README.md', 'LICENSE.md']
|
22
|
+
spec.rdoc_options = ['--line-numbers', '--inline-source', '--title', 'restcomm-ruby', '--main', 'README.md']
|
23
|
+
|
24
|
+
spec.add_dependency('multi_json', '>= 1.3.0')
|
25
|
+
spec.add_dependency('builder', '>= 2.1.2')
|
26
|
+
spec.add_dependency('jwt', '~> 1.0')
|
27
|
+
spec.add_dependency('jruby-openssl') if RUBY_PLATFORM == 'java'
|
28
|
+
# Workaround for RBX <= 2.2.1, should be fixed in next version
|
29
|
+
spec.add_dependency('rubysl') if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
32
|
+
spec.extra_rdoc_files = ['README.md', 'LICENSE.md']
|
33
|
+
spec.rdoc_options = ['--line-numbers', '--inline-source', '--title', 'restcomm-ruby', '--main', 'README.md']
|
34
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
describe Rack::RestcommWebhookAuthentication do
|
5
|
+
before do
|
6
|
+
@app = lambda {|env| [200, {'Content-Type' => 'text/plain'}, ['Hello']] }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'new' do
|
10
|
+
it 'should initialize with an app, auth token and a path' do
|
11
|
+
expect {
|
12
|
+
Rack::RestcommWebhookAuthentication.new(@app, 'ABC', /\/voice/)
|
13
|
+
}.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should initialize with an app, auth token and paths' do
|
17
|
+
expect {
|
18
|
+
Rack::RestcommWebhookAuthentication.new(@app, 'ABC', /\/voice/, /\/sms/)
|
19
|
+
}.not_to raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should initialize with an app, dynamic token and paths' do
|
23
|
+
expect {
|
24
|
+
Rack::RestcommWebhookAuthentication.new(@app, nil, /\/voice/, /\/sms/)
|
25
|
+
}.not_to raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'calling against one path with dynamic auth token' do
|
30
|
+
it 'should allow a request through if it validates' do
|
31
|
+
auth_token = 'qwerty'
|
32
|
+
account_sid = 12345
|
33
|
+
expect_any_instance_of(Rack::Request).to receive(:post?).and_return(true)
|
34
|
+
expect_any_instance_of(Rack::Request).to receive(:POST).and_return({'AccountSid' => account_sid})
|
35
|
+
@middleware = Rack::RestcommWebhookAuthentication.new(@app, nil, /\/voice/) { |asid| auth_token}
|
36
|
+
request_validator = double('RequestValidator')
|
37
|
+
expect(Restcomm::Util::RequestValidator).to receive(:new).with(auth_token).and_return(request_validator)
|
38
|
+
expect(request_validator).to receive(:validate).and_return(true)
|
39
|
+
request = Rack::MockRequest.env_for('/voice')
|
40
|
+
status, headers, body = @middleware.call(request)
|
41
|
+
expect(status).to be(200)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'calling against one path' do
|
46
|
+
before do
|
47
|
+
@middleware = Rack::RestcommWebhookAuthentication.new(
|
48
|
+
@app, 'ABC', /\/voice/
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should not intercept when the path doesn\'t match' do
|
53
|
+
expect(Restcomm::Util::RequestValidator).to_not receive(:validate)
|
54
|
+
request = Rack::MockRequest.env_for('/sms')
|
55
|
+
status, headers, body = @middleware.call(request)
|
56
|
+
expect(status).to be(200)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should allow a request through if it validates' do
|
60
|
+
expect_any_instance_of(Restcomm::Util::RequestValidator).to(
|
61
|
+
receive(:validate).and_return(true)
|
62
|
+
)
|
63
|
+
request = Rack::MockRequest.env_for('/voice')
|
64
|
+
status, headers, body = @middleware.call(request)
|
65
|
+
expect(status).to be(200)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should short circuit a request to 403 if it does not validate' do
|
69
|
+
expect_any_instance_of(Restcomm::Util::RequestValidator).to(
|
70
|
+
receive(:validate).and_return(false)
|
71
|
+
)
|
72
|
+
request = Rack::MockRequest.env_for('/voice')
|
73
|
+
status, headers, body = @middleware.call(request)
|
74
|
+
expect(status).to be(403)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'calling against many paths' do
|
79
|
+
before do
|
80
|
+
@middleware = Rack::RestcommWebhookAuthentication.new(
|
81
|
+
@app, 'ABC', /\/voice/, /\/sms/
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should not intercept when the path doesn\'t match' do
|
86
|
+
expect(Restcomm::Util::RequestValidator).to_not receive(:validate)
|
87
|
+
request = Rack::MockRequest.env_for('icesms')
|
88
|
+
status, headers, body = @middleware.call(request)
|
89
|
+
expect(status).to be(200)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'shold allow a request through if it validates' do
|
93
|
+
expect_any_instance_of(Restcomm::Util::RequestValidator).to(
|
94
|
+
receive(:validate).and_return(true)
|
95
|
+
)
|
96
|
+
request = Rack::MockRequest.env_for('/sms')
|
97
|
+
status, headers, body = @middleware.call(request)
|
98
|
+
expect(status).to be(200)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should short circuit a request to 403 if it does not validate' do
|
102
|
+
expect_any_instance_of(Restcomm::Util::RequestValidator).to(
|
103
|
+
receive(:validate).and_return(false)
|
104
|
+
)
|
105
|
+
request = Rack::MockRequest.env_for('/sms')
|
106
|
+
status, headers, body = @middleware.call(request)
|
107
|
+
expect(status).to be(403)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Restcomm::REST::Account do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@account = Restcomm::REST::Account.new('someUri', 'someClient')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'sets up incoming phone numbers resources object' do
|
10
|
+
expect(@account).to respond_to(:incoming_phone_numbers)
|
11
|
+
expect(@account.incoming_phone_numbers.instance_variable_get('@path')).to(
|
12
|
+
eq('someUri/IncomingPhoneNumbers')
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sets up an available phone numbers resources object' do
|
17
|
+
expect(@account).to respond_to(:available_phone_numbers)
|
18
|
+
expect(@account.available_phone_numbers.instance_variable_get('@path')).to(
|
19
|
+
eq('someUri/AvailablePhoneNumbers')
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'sets up an outgoing caller ids resources object' do
|
24
|
+
expect(@account).to respond_to(:outgoing_caller_ids)
|
25
|
+
expect(@account.outgoing_caller_ids.instance_variable_get('@path')).to eq(
|
26
|
+
'someUri/OutgoingCallerIds'
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'sets up a calls resources object' do
|
31
|
+
expect(@account).to respond_to(:calls)
|
32
|
+
expect(@account.calls.instance_variable_get('@path')).to eq('someUri/Calls')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'sets up a conferences resources object' do
|
36
|
+
expect(@account).to respond_to(:conferences)
|
37
|
+
expect(@account.conferences.instance_variable_get('@path')).to eq(
|
38
|
+
'someUri/Conferences'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'sets up a queues resources object' do
|
43
|
+
expect(@account).to respond_to(:queues)
|
44
|
+
expect(@account.queues.instance_variable_get('@path')).to eq(
|
45
|
+
'someUri/Queues'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'sets up a sms resource object' do
|
50
|
+
expect(@account).to respond_to(:sms)
|
51
|
+
expect(@account.sms.instance_variable_get('@path')).to eq('someUri/SMS')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'sets up a recordings resources object' do
|
55
|
+
expect(@account).to respond_to(:recordings)
|
56
|
+
expect(@account.recordings.instance_variable_get('@path')).to eq(
|
57
|
+
'someUri/Recordings'
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'sets up a transcriptions resources object' do
|
62
|
+
expect(@account).to respond_to(:transcriptions)
|
63
|
+
expect(@account.transcriptions.instance_variable_get('@path')).to eq(
|
64
|
+
'someUri/Transcriptions'
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'sets up a notifications resources object' do
|
69
|
+
expect(@account).to respond_to(:notifications)
|
70
|
+
expect(@account.notifications.instance_variable_get('@path')).to eq(
|
71
|
+
'someUri/Notifications'
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'sets up a tokens resources object' do
|
76
|
+
expect(@account).to respond_to(:tokens)
|
77
|
+
expect(@account.tokens.instance_variable_get('@path')).to eq(
|
78
|
+
'someUri/Tokens'
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'sets up an addresses resources object' do
|
83
|
+
expect(@account).to respond_to(:addresses)
|
84
|
+
expect(@account.addresses.instance_variable_get('@path')).to eq(
|
85
|
+
'someUri/Addresses'
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|