twilio-ruby 5.0.0.rc10 → 5.0.0.rc11
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 +4 -4
- data/lib/rack/twilio_webhook_authentication.rb +3 -3
- data/lib/twilio-ruby/jwt/access_token.rb +111 -0
- data/lib/twilio-ruby/{util → jwt}/capability.rb +9 -9
- data/lib/twilio-ruby/jwt/task_router.rb +203 -0
- data/lib/twilio-ruby/{util → security}/request_validator.rb +1 -1
- data/lib/twilio-ruby/version.rb +1 -1
- data/lib/twilio-ruby.rb +11 -13
- data/spec/jwt/access_token_spec.rb +114 -0
- data/spec/{util → jwt}/capability_spec.rb +18 -18
- data/spec/jwt/task_router_spec.rb +110 -0
- data/spec/jwt/task_router_taskqueue_spec.rb +111 -0
- data/spec/jwt/task_router_worker_spec.rb +146 -0
- data/spec/jwt/task_router_workspace_spec.rb +110 -0
- data/spec/rack/twilio_webhook_authentication_spec.rb +7 -7
- data/spec/{util → security}/request_validator_spec.rb +5 -5
- data/twilio-ruby.gemspec +0 -1
- metadata +19 -31
- data/lib/twilio-ruby/task_router/capability.rb +0 -87
- data/lib/twilio-ruby/task_router.rb +0 -0
- data/lib/twilio-ruby/util/access_token.rb +0 -70
- data/lib/twilio-ruby/util/client_config.rb +0 -29
- data/spec/task_router_spec.rb +0 -114
- data/spec/util/access_token_spec.rb +0 -103
- data/spec/util/client_config_spec.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08fdb6eda5b98184a808746fcedbbc14bbed427d
|
4
|
+
data.tar.gz: d66ce45e3a0742fe226e45cd515e8cd450bd423e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 362332a50a8e5870e1ff80044a1985c6a7241725d947ecb98661fa8b1414e194fea0d882d7cc82c299afe6eb027d51a58169c14c952e767e0c6ae157d4544b63
|
7
|
+
data.tar.gz: 0200bd6483b8a7e2ffbdf1a12708ad3df4eb0523e252bba3094940eb4cfe689f01d23452c41bd8a66931a658b6ab2e003659044c4e950edfe601824ef56ff7ea
|
@@ -25,12 +25,12 @@ module Rack
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def call(env)
|
28
|
-
return @app.call(env) unless env[
|
28
|
+
return @app.call(env) unless env['PATH_INFO'].match(@path_regex)
|
29
29
|
request = Rack::Request.new(env)
|
30
30
|
original_url = request.url
|
31
31
|
params = request.post? ? request.POST : {}
|
32
32
|
auth_token = @auth_token || get_auth_token(params['AccountSid'])
|
33
|
-
validator = Twilio::
|
33
|
+
validator = Twilio::Security::RequestValidator.new(auth_token)
|
34
34
|
signature = env['HTTP_X_TWILIO_SIGNATURE'] || ""
|
35
35
|
if validator.validate(original_url, params, signature)
|
36
36
|
@app.call(env)
|
@@ -38,7 +38,7 @@ module Rack
|
|
38
38
|
[
|
39
39
|
403,
|
40
40
|
{'Content-Type' => 'text/plain'},
|
41
|
-
[
|
41
|
+
['Twilio Request Validation Failed.']
|
42
42
|
]
|
43
43
|
end
|
44
44
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Twilio
|
2
|
+
module JWT
|
3
|
+
class AccessToken
|
4
|
+
attr_accessor :account_sid,
|
5
|
+
:signing_key_id,
|
6
|
+
:secret,
|
7
|
+
:ttl,
|
8
|
+
:identity,
|
9
|
+
:nbf
|
10
|
+
|
11
|
+
def initialize(account_sid, signing_key_id, secret, ttl=3600, identity=nil, nbf=nil)
|
12
|
+
@account_sid = account_sid
|
13
|
+
@signing_key_sid = signing_key_id
|
14
|
+
@secret = secret
|
15
|
+
@ttl = ttl
|
16
|
+
@identity = identity
|
17
|
+
@nbf = nbf
|
18
|
+
@grants = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_grant(grant)
|
22
|
+
@grants.push(grant)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_jwt(algorithm='HS256')
|
26
|
+
now = Time.now.to_i - 1
|
27
|
+
headers = {
|
28
|
+
cty: 'twilio-fpa;v=1',
|
29
|
+
typ: 'JWT'
|
30
|
+
}
|
31
|
+
|
32
|
+
grants = {}
|
33
|
+
if @identity
|
34
|
+
grants[:identity] = @identity
|
35
|
+
end
|
36
|
+
|
37
|
+
@grants.each { |grant| grants[grant.key] = grant.payload }
|
38
|
+
|
39
|
+
payload = {
|
40
|
+
jti: "#{@signing_key_sid}-#{now}",
|
41
|
+
iss: @signing_key_sid,
|
42
|
+
sub: @account_sid,
|
43
|
+
exp: now + @ttl,
|
44
|
+
grants: grants
|
45
|
+
}
|
46
|
+
|
47
|
+
payload[:nbf] = @nbf unless @nbf.nil?
|
48
|
+
|
49
|
+
::JWT.encode payload, @secret, algorithm, headers
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
to_jwt
|
54
|
+
end
|
55
|
+
|
56
|
+
class ConversationsGrant
|
57
|
+
attr_accessor :configuration_profile_sid
|
58
|
+
|
59
|
+
def key
|
60
|
+
'rtc'
|
61
|
+
end
|
62
|
+
|
63
|
+
def payload
|
64
|
+
payload = {}
|
65
|
+
|
66
|
+
if configuration_profile_sid
|
67
|
+
payload[:configuration_profile_sid] = configuration_profile_sid
|
68
|
+
end
|
69
|
+
|
70
|
+
payload
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class IpMessagingGrant
|
76
|
+
attr_accessor :service_sid,
|
77
|
+
:endpoint_id,
|
78
|
+
:deployment_role_sid,
|
79
|
+
:push_credential_sid
|
80
|
+
|
81
|
+
def key
|
82
|
+
'ip_messaging'
|
83
|
+
end
|
84
|
+
|
85
|
+
def payload
|
86
|
+
payload = {}
|
87
|
+
|
88
|
+
if service_sid
|
89
|
+
payload[:service_sid] = service_sid
|
90
|
+
end
|
91
|
+
|
92
|
+
if endpoint_id
|
93
|
+
payload[:endpoint_id] = endpoint_id
|
94
|
+
end
|
95
|
+
|
96
|
+
if deployment_role_sid
|
97
|
+
payload[:deployment_role_sid] = deployment_role_sid
|
98
|
+
end
|
99
|
+
|
100
|
+
if push_credential_sid
|
101
|
+
payload[:push_credential_sid] = push_credential_sid
|
102
|
+
end
|
103
|
+
|
104
|
+
payload
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Twilio
|
2
|
-
module
|
2
|
+
module JWT
|
3
3
|
class Capability
|
4
4
|
|
5
5
|
include Twilio::Util
|
@@ -15,21 +15,21 @@ module Twilio
|
|
15
15
|
|
16
16
|
def allow_client_incoming(client_name)
|
17
17
|
@client_name = client_name # stash for use in outgoing
|
18
|
-
scope_params = {
|
18
|
+
scope_params = { clientName: client_name }
|
19
19
|
@capabilities << scope_uri_for('client', 'incoming', scope_params)
|
20
20
|
end
|
21
21
|
|
22
22
|
def allow_client_outgoing(app_sid, params = {})
|
23
23
|
@allow_client_outgoing = true
|
24
|
-
@outgoing_scope_params = {
|
24
|
+
@outgoing_scope_params = { appSid: app_sid }
|
25
25
|
unless params.empty?
|
26
26
|
@outgoing_scope_params['appParams'] = url_encode params
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
def allow_event_stream(filters = {})
|
31
|
-
scope_params = {
|
32
|
-
scope_params[
|
31
|
+
scope_params = { path: '/2010-04-01/Events' }
|
32
|
+
scope_params[:params] = filters unless filters.empty?
|
33
33
|
@capabilities << scope_uri_for('stream', 'subscribe', scope_params)
|
34
34
|
end
|
35
35
|
|
@@ -50,12 +50,12 @@ module Twilio
|
|
50
50
|
end
|
51
51
|
|
52
52
|
payload = {
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
scope: capabilities.join(' '),
|
54
|
+
iss: @account_sid,
|
55
|
+
exp: (Time.now.to_i + ttl),
|
56
56
|
}
|
57
57
|
|
58
|
-
JWT.encode payload, @auth_token
|
58
|
+
::JWT.encode payload, @auth_token
|
59
59
|
|
60
60
|
end
|
61
61
|
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Twilio
|
2
|
+
module JWT
|
3
|
+
class TaskRouterCapability
|
4
|
+
TASK_ROUTER_BASE_URL = 'https://taskrouter.twilio.com'
|
5
|
+
TASK_ROUTER_VERSION = 'v1'
|
6
|
+
TASK_ROUTER_WEBSOCKET_BASE_URL = 'https://event-bridge.twilio.com/v1/wschannels'
|
7
|
+
|
8
|
+
REQUIRED = {required: true}
|
9
|
+
OPTIONAL = {required: false}
|
10
|
+
|
11
|
+
def initialize(account_sid, auth_token, workspace_sid, channel_id)
|
12
|
+
@account_sid = account_sid
|
13
|
+
@auth_token = auth_token
|
14
|
+
@policies = []
|
15
|
+
|
16
|
+
@workspace_sid = workspace_sid
|
17
|
+
@channel_id = channel_id
|
18
|
+
|
19
|
+
@base_url = "#{TASK_ROUTER_BASE_URL}/#{TASK_ROUTER_VERSION}/Workspaces/#{workspace_sid}"
|
20
|
+
|
21
|
+
validate_jwt
|
22
|
+
|
23
|
+
setup_resource
|
24
|
+
|
25
|
+
allow_websocket_requests(@channel_id)
|
26
|
+
allow(@resource_url, 'GET')
|
27
|
+
end
|
28
|
+
|
29
|
+
def allow_fetch_subresources
|
30
|
+
allow(@resource_url + '/**', 'GET')
|
31
|
+
end
|
32
|
+
|
33
|
+
def allow_updates
|
34
|
+
allow(@resource_url, 'POST')
|
35
|
+
end
|
36
|
+
|
37
|
+
def allow_updates_subresources
|
38
|
+
allow(@resource_url + '/**', 'POST')
|
39
|
+
end
|
40
|
+
|
41
|
+
def allow_delete
|
42
|
+
allow(@resource_url, 'DELETE')
|
43
|
+
end
|
44
|
+
|
45
|
+
def allow_delete_subresources
|
46
|
+
allow(@resource_url + '/**', 'DELETE')
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_policy(url, method, allowed = true, query_filters = nil, post_filters = nil)
|
50
|
+
policy = {
|
51
|
+
url: url,
|
52
|
+
method: method,
|
53
|
+
query_filter: query_filters || {},
|
54
|
+
post_filter: post_filters || {},
|
55
|
+
allow: allowed
|
56
|
+
}
|
57
|
+
|
58
|
+
@policies.push(policy)
|
59
|
+
end
|
60
|
+
|
61
|
+
def allow(url, method, query_filters = nil, post_filters = nil)
|
62
|
+
add_policy(url, method, true, query_filters, post_filters)
|
63
|
+
end
|
64
|
+
|
65
|
+
def deny(url, method, query_filters = nil, post_filters = nil)
|
66
|
+
add_policy(url, method, false, query_filters, post_filters)
|
67
|
+
end
|
68
|
+
|
69
|
+
def generate_token(ttl = 3600)
|
70
|
+
task_router_attributes = {
|
71
|
+
account_sid: @account_sid,
|
72
|
+
workspace_sid: @workspace_sid,
|
73
|
+
channel: @channel_id
|
74
|
+
}
|
75
|
+
|
76
|
+
if @channel_id[0..1] == 'WK'
|
77
|
+
task_router_attributes[:worker_sid] = @channel_id
|
78
|
+
elsif @channel_id[0..1] == 'WQ'
|
79
|
+
task_router_attributes[:taskqueue_sid] = @channel_id
|
80
|
+
end
|
81
|
+
|
82
|
+
generate_token_protected(ttl, task_router_attributes)
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def generate_token_protected(ttl = 3600, extra_attributes)
|
88
|
+
payload = {
|
89
|
+
iss: @account_sid,
|
90
|
+
exp: (Time.now.to_i + ttl),
|
91
|
+
version: TASK_ROUTER_VERSION,
|
92
|
+
friendly_name: @channel_id,
|
93
|
+
policies: @policies,
|
94
|
+
}
|
95
|
+
extra_attributes.each { |key, value|
|
96
|
+
payload[key] = value
|
97
|
+
}
|
98
|
+
|
99
|
+
::JWT.encode payload, @auth_token
|
100
|
+
end
|
101
|
+
|
102
|
+
def setup_resource
|
103
|
+
if @channel_id[0..1] == 'WS'
|
104
|
+
@resource_url = @base_url
|
105
|
+
elsif @channel_id[0..1] == 'WK'
|
106
|
+
@resource_url = @base_url + '/Workers/' + @channel_id
|
107
|
+
|
108
|
+
@activity_url = @base_url + '/Activities'
|
109
|
+
allow(@activity_url, 'GET')
|
110
|
+
|
111
|
+
@tasks_url = @base_url + '/Tasks/**'
|
112
|
+
allow(@tasks_url, 'GET')
|
113
|
+
|
114
|
+
@worker_reservations_url = @resource_url + '/Reservations/**'
|
115
|
+
allow(@worker_reservations_url, 'GET')
|
116
|
+
|
117
|
+
elsif @channel_id[0..1] == 'WQ'
|
118
|
+
@resource_url = @base_url + '/TaskQueues/' + @channel_id
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def allow_websocket_requests(channel_id)
|
123
|
+
worker_url = "#{TASK_ROUTER_WEBSOCKET_BASE_URL}/#{@account_sid}/#{channel_id}"
|
124
|
+
['GET', 'POST'].each do |meth|
|
125
|
+
add_policy(worker_url, meth)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def validate_jwt
|
130
|
+
if @account_sid.nil? or @account_sid[0..1] != 'AC'
|
131
|
+
raise "Invalid AccountSid provided #{@account_sid}"
|
132
|
+
end
|
133
|
+
if @workspace_sid.nil? or @workspace_sid[0..1] != 'WS'
|
134
|
+
raise "Invalid WorkspaceSid provided #{@workspace_sid}"
|
135
|
+
end
|
136
|
+
if @channel_id.nil?
|
137
|
+
raise 'ChannelId not provided'
|
138
|
+
end
|
139
|
+
@prefix = @channel_id[0..1]
|
140
|
+
if @prefix != 'WS' and @prefix != 'WK' and @prefix != 'WQ'
|
141
|
+
raise "Invalid ChannelId provided: #{@channel_id}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class WorkerCapability < TaskRouterCapability
|
147
|
+
|
148
|
+
def initialize(account_sid, auth_token, workspace_sid, worker_sid)
|
149
|
+
super(account_sid, auth_token, workspace_sid, worker_sid)
|
150
|
+
@tasks_url = @base_url + '/Tasks/**'
|
151
|
+
@activity_url = @base_url + '/Activities'
|
152
|
+
@worker_reservations_url = @resource_url + '/Reservations/**'
|
153
|
+
|
154
|
+
allow(@activity_url, 'GET')
|
155
|
+
allow(@tasks_url, 'GET')
|
156
|
+
allow(@worker_reservations_url, 'GET')
|
157
|
+
end
|
158
|
+
|
159
|
+
def allow_activity_updates
|
160
|
+
allow(@resource_url, 'POST', nil, {ActivitySid: REQUIRED})
|
161
|
+
end
|
162
|
+
|
163
|
+
def allow_reservation_updates
|
164
|
+
allow(@tasks_url, 'POST', nil, nil)
|
165
|
+
allow(@worker_reservations_url, 'POST', nil, nil)
|
166
|
+
end
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
def setup_resource
|
171
|
+
@resource_url = @base_url + '/Workers/' + @channel_id
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
class WorkspaceCapability < TaskRouterCapability
|
177
|
+
|
178
|
+
def initialize(account_sid, auth_token, workspace_sid)
|
179
|
+
super(account_sid, auth_token, workspace_sid, workspace_sid)
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
|
184
|
+
def setup_resource
|
185
|
+
@resource_url = @base_url
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
class TaskQueueCapability < TaskRouterCapability
|
191
|
+
|
192
|
+
def initialize(account_sid, auth_token, workspace_sid, taskqueue_sid)
|
193
|
+
super(account_sid, auth_token, workspace_sid, taskqueue_sid)
|
194
|
+
end
|
195
|
+
|
196
|
+
protected
|
197
|
+
|
198
|
+
def setup_resource
|
199
|
+
@resource_url = @base_url + '/TaskQueues/' + @channel_id
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
data/lib/twilio-ruby/version.rb
CHANGED
data/lib/twilio-ruby.rb
CHANGED
@@ -6,38 +6,36 @@ require 'openssl'
|
|
6
6
|
require 'base64'
|
7
7
|
require 'forwardable'
|
8
8
|
require 'jwt'
|
9
|
+
require 'time'
|
9
10
|
|
10
11
|
require 'twilio-ruby/version' unless defined?(Twilio::VERSION)
|
11
12
|
require 'rack/twilio_webhook_authentication'
|
12
13
|
|
13
14
|
require 'twilio-ruby/util'
|
14
|
-
require 'twilio-ruby/
|
15
|
-
require 'twilio-ruby/
|
15
|
+
require 'twilio-ruby/jwt/access_token'
|
16
|
+
require 'twilio-ruby/jwt/capability'
|
17
|
+
require 'twilio-ruby/jwt/task_router'
|
18
|
+
require 'twilio-ruby/security/request_validator'
|
16
19
|
require 'twilio-ruby/util/configuration'
|
17
|
-
|
18
|
-
require 'twilio-ruby/util/access_token'
|
20
|
+
|
19
21
|
require 'twilio-ruby/twiml/response'
|
20
22
|
|
21
|
-
Dir[File.dirname(__FILE__) +
|
22
|
-
require file
|
23
|
-
end
|
24
|
-
Dir[File.dirname(__FILE__) + "/twilio-ruby/framework/**/*.rb"].each do |file|
|
23
|
+
Dir[File.dirname(__FILE__) + '/twilio-ruby/http/**/*.rb'].each do |file|
|
25
24
|
require file
|
26
25
|
end
|
27
|
-
Dir[File.dirname(__FILE__) +
|
26
|
+
Dir[File.dirname(__FILE__) + '/twilio-ruby/framework/**/*.rb'].each do |file|
|
28
27
|
require file
|
29
28
|
end
|
30
|
-
Dir[File.dirname(__FILE__) +
|
29
|
+
Dir[File.dirname(__FILE__) + '/twilio-ruby/rest/*.rb'].each do |file|
|
31
30
|
require file
|
32
31
|
end
|
33
|
-
Dir[File.dirname(__FILE__) +
|
32
|
+
Dir[File.dirname(__FILE__) + '/twilio-ruby/rest/**/*.rb'].each do |file|
|
34
33
|
require file
|
35
34
|
end
|
36
|
-
Dir[File.dirname(__FILE__) +
|
35
|
+
Dir[File.dirname(__FILE__) + '/twilio-ruby/compatibility/**/*.rb'].each do |file|
|
37
36
|
require file
|
38
37
|
end
|
39
38
|
|
40
|
-
|
41
39
|
module Twilio
|
42
40
|
extend SingleForwardable
|
43
41
|
|