restcomm-ruby 1.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.
- 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
|