ribbon-intercom 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ribbon/intercom/client/mock_sdk.rb +13 -0
- data/lib/ribbon/intercom/client/sdk/adapters/adapter/response.rb +38 -0
- data/lib/ribbon/intercom/client/sdk/adapters/adapter.rb +51 -0
- data/lib/ribbon/intercom/client/sdk/adapters/http_adapter/connection.rb +34 -0
- data/lib/ribbon/intercom/client/sdk/adapters/http_adapter.rb +26 -0
- data/lib/ribbon/intercom/client/sdk/adapters/local_adapter.rb +54 -0
- data/lib/ribbon/intercom/client/sdk/adapters/mock_adapter.rb +40 -0
- data/lib/ribbon/intercom/client/sdk/adapters.rb +10 -0
- data/lib/ribbon/intercom/client/sdk.rb +71 -26
- data/lib/ribbon/intercom/client.rb +35 -7
- data/lib/ribbon/intercom/errors.rb +34 -6
- data/lib/ribbon/intercom/package.rb +64 -0
- data/lib/ribbon/intercom/packageable/mixin.rb +35 -0
- data/lib/ribbon/intercom/packageable.rb +6 -0
- data/lib/ribbon/intercom/service/channel/stores/mock_store.rb +40 -0
- data/lib/ribbon/intercom/service/channel/stores/redis_store.rb +186 -0
- data/lib/ribbon/intercom/service/{channel_stores/base.rb → channel/stores/store.rb} +13 -5
- data/lib/ribbon/intercom/service/channel/stores.rb +9 -0
- data/lib/ribbon/intercom/service/channel.rb +106 -4
- data/lib/ribbon/intercom/service.rb +156 -95
- data/lib/ribbon/intercom/utils/mixins/mock_safe.rb +26 -0
- data/lib/ribbon/intercom/utils/mixins.rb +5 -0
- data/lib/ribbon/intercom/utils/signer.rb +71 -0
- data/lib/ribbon/intercom/utils.rb +40 -13
- data/lib/ribbon/intercom/version.rb +1 -1
- data/lib/ribbon/intercom.rb +10 -7
- data/lib/tasks/intercom.rake +3 -3
- metadata +20 -20
- data/lib/ribbon/intercom/client/sdk/connection.rb +0 -35
- data/lib/ribbon/intercom/client/sdk/response.rb +0 -59
- data/lib/ribbon/intercom/service/channel_stores/redis_store.rb +0 -60
@@ -6,31 +6,21 @@ module Ribbon::Intercom
|
|
6
6
|
class Service
|
7
7
|
autoload(:Channel, 'ribbon/intercom/service/channel')
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
autoload(:RedisStore, 'ribbon/intercom/service/channel_stores/redis_store')
|
12
|
-
end
|
9
|
+
# Used to signify an empty body
|
10
|
+
class EmptyResponse; end
|
13
11
|
|
14
12
|
class << self
|
15
13
|
def instance
|
16
14
|
@instance ||= new(store: _load_store)
|
17
15
|
end
|
18
16
|
|
19
|
-
def
|
20
|
-
|
21
|
-
@_store_params = params
|
22
|
-
end
|
23
|
-
|
24
|
-
def permissions(*perms)
|
25
|
-
@_next_permissions = perms.map(&:to_sym).to_set
|
17
|
+
def mock
|
18
|
+
Client::MockSDK.new(self)
|
26
19
|
end
|
27
20
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
else
|
32
|
-
{}
|
33
|
-
end.merge(@_method_permissions).dup
|
21
|
+
def store(store_name, params={})
|
22
|
+
@_store_name = store_name
|
23
|
+
@_store_params = params
|
34
24
|
end
|
35
25
|
|
36
26
|
# The call method is needed here because Rails checks to see if a mounted
|
@@ -40,19 +30,20 @@ module Ribbon::Intercom
|
|
40
30
|
end
|
41
31
|
|
42
32
|
def method_missing(meth, *args, &block)
|
43
|
-
instance.
|
33
|
+
instance.public_send(meth, *args, &block)
|
44
34
|
end
|
45
35
|
|
46
36
|
def _load_store
|
47
37
|
raise "Store name missing" unless (store_name = @_store_name.to_s)
|
48
38
|
|
49
39
|
store = Utils.classify(store_name) + "Store"
|
50
|
-
Intercom::Service::
|
40
|
+
Intercom::Service::Channel::Stores.const_get(store).new(@_store_params)
|
51
41
|
end
|
52
42
|
end # Class methods
|
53
43
|
|
54
44
|
attr_reader :request
|
55
45
|
attr_reader :channel
|
46
|
+
attr_reader :subject
|
56
47
|
attr_reader :env
|
57
48
|
|
58
49
|
def initialize(opts={})
|
@@ -65,36 +56,17 @@ module Ribbon::Intercom
|
|
65
56
|
|
66
57
|
def open_channel(params={})
|
67
58
|
# Accept either an array of permissions or a string
|
68
|
-
store.open_channel(params)
|
59
|
+
store.open_channel(params).tap { |channel|
|
60
|
+
channel.may(Utils.method_identifier(self, :rotate_secret))
|
61
|
+
}
|
69
62
|
end
|
70
63
|
|
71
64
|
def lookup_channel(token)
|
72
65
|
store.lookup_channel(token)
|
73
66
|
end
|
74
67
|
|
75
|
-
def method_permissions(method_name)
|
76
|
-
self.class.method_permissions[method_name.to_sym]
|
77
|
-
end
|
78
|
-
|
79
|
-
def method_permissions!(method_name)
|
80
|
-
method_permissions(method_name) or
|
81
|
-
_respond!(404, {}, "Method requested not found.")
|
82
|
-
end
|
83
|
-
|
84
68
|
def sufficient_permissions?(intercom_method)
|
85
|
-
|
86
|
-
channel.may?(*permissions)
|
87
|
-
end
|
88
|
-
|
89
|
-
def sufficient_permissions!(intercom_method)
|
90
|
-
sufficient_permissions?(intercom_method) or
|
91
|
-
_respond!(
|
92
|
-
403,
|
93
|
-
{
|
94
|
-
"X-Intercom-Permissions-Missing" => _missing_permissions(intercom_method).to_a.join(',')
|
95
|
-
},
|
96
|
-
"Permissions missing."
|
97
|
-
)
|
69
|
+
channel.may?(Utils.method_identifier(subject, intercom_method))
|
98
70
|
end
|
99
71
|
|
100
72
|
def call(env)
|
@@ -105,46 +77,122 @@ module Ribbon::Intercom
|
|
105
77
|
@env = env
|
106
78
|
|
107
79
|
response = catch(:response) {
|
108
|
-
|
109
|
-
|
110
|
-
|
80
|
+
begin
|
81
|
+
intercom_method, args = _process_request
|
82
|
+
_call_method(intercom_method, *args)
|
83
|
+
rescue Exception => error
|
84
|
+
_respond_with_error!(error)
|
85
|
+
end
|
111
86
|
}
|
112
87
|
|
113
88
|
response.finish
|
114
|
-
rescue Exception => e
|
115
|
-
_response(500, {}, e.message).finish
|
116
89
|
end
|
117
90
|
|
118
|
-
def
|
119
|
-
|
91
|
+
def rotate_secret
|
92
|
+
channel.rotate_secret!
|
120
93
|
end
|
121
94
|
|
122
95
|
private
|
123
96
|
|
124
|
-
def
|
97
|
+
def _process_request
|
98
|
+
_init_request
|
99
|
+
_authenticate_request!
|
100
|
+
_load_subject
|
101
|
+
[_load_method, _load_args]
|
102
|
+
end
|
103
|
+
|
104
|
+
def _init_request
|
125
105
|
@request = Rack::Request.new(env)
|
126
|
-
|
106
|
+
|
107
|
+
unless request.put?
|
108
|
+
_error!(Errors::MethodNotAllowedError, 'only PUT allowed')
|
109
|
+
end
|
127
110
|
end
|
128
111
|
|
129
|
-
def
|
130
|
-
_request_authenticated
|
112
|
+
def _authenticate_request!
|
113
|
+
unless _request_authenticated?
|
114
|
+
_error!(Errors::AuthenticationError, "invalid channel credentials")
|
115
|
+
end
|
116
|
+
end
|
131
117
|
|
132
|
-
|
133
|
-
|
134
|
-
|
118
|
+
def _load_subject
|
119
|
+
if (encoded_subject=env['HTTP_X_INTERCOM_SUBJECT']) && !encoded_subject.empty?
|
120
|
+
@subject = _decode_subject(encoded_subject)
|
121
|
+
_error!(Errors::InvalidSubjectSignatureError) unless @subject
|
122
|
+
else
|
123
|
+
@subject = self
|
124
|
+
end
|
125
|
+
end
|
135
126
|
|
136
|
-
|
127
|
+
def _load_method
|
128
|
+
env['HTTP_X_INTERCOM_METHOD'].to_sym.tap { |intercom_method|
|
129
|
+
_sufficient_permissions!(intercom_method)
|
130
|
+
}
|
137
131
|
end
|
138
132
|
|
139
|
-
def
|
140
|
-
body
|
141
|
-
_respond!(200, {}, body)
|
133
|
+
def _load_args
|
134
|
+
_decode_args(request.body.read)
|
142
135
|
rescue Errors::UnsafeValueError
|
143
|
-
|
136
|
+
_error!(Errors::UnsafeArgumentError, "One or more argument type is invalid.")
|
144
137
|
end
|
145
138
|
|
146
|
-
|
147
|
-
|
139
|
+
##
|
140
|
+
# Call the method on the subject with the args.
|
141
|
+
def _call_method(intercom_method, *args)
|
142
|
+
body = _package(subject.public_send(intercom_method, *args))
|
143
|
+
|
144
|
+
headers = {}
|
145
|
+
unless subject == self
|
146
|
+
# Need to send subject back in case it was modified by the method.
|
147
|
+
headers['X-Intercom-Subject'] = _encode_subject(subject)
|
148
|
+
|
149
|
+
if subject.is_a?(Packageable::Mixin)
|
150
|
+
# Need to send the package data back in case it changed, too.
|
151
|
+
headers['X-Intercom-Package-Data'] = subject.encoded_package_data
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
_respond!(200, headers, body)
|
156
|
+
rescue NoMethodError => error
|
157
|
+
# Want to respond with a 404 here.
|
158
|
+
_error!(Errors::InvalidMethodError, intercom_method)
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Package up any non-basic objects that include Packageable::Mixin.
|
163
|
+
def _package(object)
|
164
|
+
Utils.walk(object) { |object, context|
|
165
|
+
if Utils.basic_type?(object)
|
166
|
+
object
|
167
|
+
elsif context == :hash_key
|
168
|
+
# Hash keys must be basic types.
|
169
|
+
_error!(Errors::UnsafeResponseError, object.inspect)
|
170
|
+
elsif object.is_a?(Packageable::Mixin)
|
171
|
+
_package_obj(object, object.package_data)
|
172
|
+
elsif object.is_a?(Class) && object < Packageable::Mixin
|
173
|
+
_package_obj(object)
|
174
|
+
else
|
175
|
+
_error!(Errors::UnsafeResponseError, object.inspect)
|
176
|
+
end
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
def _package_obj(object, data=nil)
|
181
|
+
Package.new(_encode_subject(object), data)
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Marshal dumps the subject and signs the resulting bytes with the channel.
|
186
|
+
def _encode_subject(subject)
|
187
|
+
Base64.strict_encode64(channel.sign(Marshal.dump(subject)))
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# Decodes the subject sent from the client.
|
192
|
+
def _decode_subject(encoded_subject)
|
193
|
+
signed_subject = Base64.strict_decode64(encoded_subject)
|
194
|
+
marshalled_subject = channel.verify(signed_subject)
|
195
|
+
marshalled_subject && Marshal.load(marshalled_subject)
|
148
196
|
end
|
149
197
|
|
150
198
|
def _request_authenticated?
|
@@ -160,56 +208,69 @@ module Ribbon::Intercom
|
|
160
208
|
end
|
161
209
|
end
|
162
210
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
211
|
+
def _sufficient_permissions!(intercom_method)
|
212
|
+
unless sufficient_permissions?(intercom_method)
|
213
|
+
required = Utils.method_identifier(subject, intercom_method)
|
214
|
+
_error!(Errors::InsufficientPermissionsError, required)
|
215
|
+
end
|
168
216
|
end
|
169
217
|
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
|
218
|
+
def _response(status, headers={}, body=EmptyResponse)
|
219
|
+
body = body == EmptyResponse ? [] : [_encode_body(body)]
|
220
|
+
headers = headers.merge("Content-Type" => "text/plain", "Transfer-Encoding" => "gzip")
|
221
|
+
Rack::Response.new(body, status, headers)
|
174
222
|
end
|
175
223
|
|
176
|
-
def
|
177
|
-
|
224
|
+
def _respond!(status, headers={}, body=EmptyResponse)
|
225
|
+
throw :response, _response(status, headers, body)
|
178
226
|
end
|
179
227
|
|
180
|
-
def
|
181
|
-
|
182
|
-
headers = headers.merge("Content-Type" => "text/plain", "Transfer-Encoding" => "gzip")
|
183
|
-
Rack::Response.new(body, status, headers)
|
228
|
+
def _respond_with_error!(error, status=500)
|
229
|
+
_respond!(status, { 'X-Intercom-Error' => _encode_error(error) })
|
184
230
|
end
|
185
231
|
|
186
|
-
def
|
187
|
-
|
232
|
+
def _error!(klass, message=nil)
|
233
|
+
error = message ? klass.new(message) : klass.new
|
234
|
+
_respond_with_error!(error, _error_to_http_code(error))
|
235
|
+
end
|
236
|
+
|
237
|
+
def _error_to_http_code(error)
|
238
|
+
case error
|
239
|
+
when Errors::MethodNotAllowedError
|
240
|
+
405
|
241
|
+
when Errors::NotFoundError
|
242
|
+
404
|
243
|
+
when Errors::ForbiddenError
|
244
|
+
403
|
245
|
+
when Errors::AuthenticationError
|
246
|
+
401
|
247
|
+
when Errors::RequestError
|
248
|
+
400
|
249
|
+
when Errors::ServerError
|
250
|
+
500
|
251
|
+
else
|
252
|
+
500
|
253
|
+
end
|
188
254
|
end
|
189
255
|
|
190
256
|
def _encode_body(body)
|
191
257
|
Base64.strict_encode64(Marshal.dump(body))
|
192
258
|
end
|
193
259
|
|
260
|
+
def _encode_error(error)
|
261
|
+
Base64.strict_encode64(Marshal.dump(error))
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# Decodes the arguments.
|
266
|
+
#
|
267
|
+
# It's very important that this happens *after* channel authentication is
|
268
|
+
# performed. Since `args` comes from the client it could contain malicious
|
269
|
+
# marshalled data.
|
194
270
|
def _decode_args(args)
|
195
271
|
Utils.sanitize(Marshal.load(Base64.strict_decode64(args))).tap { |args|
|
196
272
|
raise Errors::UnsafeValueError unless args.is_a?(Array)
|
197
273
|
}
|
198
274
|
end
|
199
275
|
end # Service
|
200
|
-
|
201
|
-
# Re-opening the class so the above instance methods don't get added to the
|
202
|
-
# permissions hash
|
203
|
-
class Service
|
204
|
-
class << self
|
205
|
-
def method_added(method_name)
|
206
|
-
(@_method_permissions ||= {})[method_name] = (@_next_permissions || [method_name].to_set).dup.freeze
|
207
|
-
@_next_permissions = nil
|
208
|
-
end
|
209
|
-
end # Class Methods
|
210
|
-
|
211
|
-
def rotate_secret
|
212
|
-
channel.rotate_secret!
|
213
|
-
end
|
214
|
-
end # Service
|
215
276
|
end # Ribbon::Intercom
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
module Utils::Mixins
|
3
|
+
module MockSafe
|
4
|
+
##
|
5
|
+
# Return a mock safe version of this package.
|
6
|
+
def mock_safe
|
7
|
+
dup.tap { |obj| obj.mock_safe! }
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Make this package mock safe.
|
12
|
+
def mock_safe!
|
13
|
+
unless mock_safe?
|
14
|
+
@_mock_safe = true
|
15
|
+
|
16
|
+
# For RSpec: Allow any method to be mocked on this instance.
|
17
|
+
define_singleton_method(:respond_to?) { |*args| true }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def mock_safe?
|
22
|
+
!!@_mock_safe
|
23
|
+
end
|
24
|
+
end # MockSafe
|
25
|
+
end # Utils::Mixin
|
26
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module Ribbon::Intercom
|
5
|
+
module Utils
|
6
|
+
class Signer
|
7
|
+
class << self
|
8
|
+
def random_key
|
9
|
+
SecureRandom.random_bytes(32)
|
10
|
+
end
|
11
|
+
|
12
|
+
def random_salt
|
13
|
+
SecureRandom.random_bytes(8)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :key
|
18
|
+
|
19
|
+
def initialize(key=self.class.random_key)
|
20
|
+
raise ArgumentError, "key must be defined" unless key
|
21
|
+
@key = key.dup.freeze
|
22
|
+
@_digest = OpenSSL::Digest::SHA256.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def sign(data)
|
26
|
+
unless data.is_a?(String) && data.encoding == Encoding::BINARY
|
27
|
+
raise ArgumentError, "data must be a binary encoded string"
|
28
|
+
end
|
29
|
+
|
30
|
+
salt = self.class.random_salt
|
31
|
+
signature = _sign(salt, data)
|
32
|
+
_encode(signature, salt, data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def verify(signed_data)
|
36
|
+
unless signed_data.is_a?(String) && signed_data.encoding == Encoding::BINARY
|
37
|
+
raise ArgumentError, "signed_data must be a binary encoded string"
|
38
|
+
end
|
39
|
+
|
40
|
+
signature, salt, data = _decode(signed_data)
|
41
|
+
data if _sign(salt, data) == signature
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def _sign(salt, data)
|
47
|
+
OpenSSL::HMAC.digest(@_digest, key, salt + data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def _encode(signature, salt, data)
|
51
|
+
"\x01" + signature + salt + data
|
52
|
+
end
|
53
|
+
|
54
|
+
def _decode(signed_data)
|
55
|
+
index = 0
|
56
|
+
version = signed_data[index]
|
57
|
+
|
58
|
+
index += version.length
|
59
|
+
signature = signed_data.slice(index, @_digest.length)
|
60
|
+
|
61
|
+
index += signature.length
|
62
|
+
salt = signed_data.slice(index, 8)
|
63
|
+
|
64
|
+
index += salt.length
|
65
|
+
data = signed_data.slice(index..-1)
|
66
|
+
|
67
|
+
[signature, salt, data]
|
68
|
+
end
|
69
|
+
end # Signer
|
70
|
+
end # Utils
|
71
|
+
end # Ribbon::Intercom
|
@@ -1,27 +1,47 @@
|
|
1
1
|
require 'date'
|
2
2
|
|
3
3
|
module Ribbon::Intercom
|
4
|
-
|
4
|
+
module Utils
|
5
|
+
autoload(:Signer, 'ribbon/intercom/utils/signer')
|
6
|
+
autoload(:Mixins, 'ribbon/intercom/utils/mixins')
|
7
|
+
|
8
|
+
BASIC_TYPES = [
|
9
|
+
String, Symbol, TrueClass, FalseClass, Integer, Float, NilClass, Date, Time, DateTime
|
10
|
+
].freeze
|
11
|
+
|
5
12
|
class << self
|
6
|
-
def
|
13
|
+
def basic_type?(object)
|
14
|
+
case object
|
15
|
+
when *BASIC_TYPES then true
|
16
|
+
else false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def walk(object, context=nil, &block)
|
7
21
|
case object
|
8
|
-
when String, Symbol, TrueClass, FalseClass, Integer, Float, NilClass, Date, Time, DateTime
|
9
|
-
object
|
10
22
|
when Hash
|
11
|
-
|
23
|
+
Hash[
|
24
|
+
object.map { |key, val|
|
25
|
+
[walk(key, :hash_key, &block), walk(val, :hash_value, &block)]
|
26
|
+
}
|
27
|
+
]
|
12
28
|
when Array
|
13
|
-
|
29
|
+
object.map { |obj| walk(obj, :array_elem, &block) }
|
14
30
|
else
|
15
|
-
|
31
|
+
yield object, context
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
35
|
+
##
|
36
|
+
# Raises an error if the object is or contains any non-basic types.
|
37
|
+
def sanitize(object)
|
38
|
+
walk(object) { |object|
|
39
|
+
if basic_type?(object)
|
40
|
+
object
|
41
|
+
else
|
42
|
+
raise Errors::UnsafeValueError, object.inspect
|
43
|
+
end
|
44
|
+
}
|
25
45
|
end
|
26
46
|
|
27
47
|
def symbolize_keys(hash)
|
@@ -31,6 +51,13 @@ module Ribbon::Intercom
|
|
31
51
|
def classify(str)
|
32
52
|
str.split("_").map(&:capitalize).join
|
33
53
|
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Returns an identifier for the method (e.g., A::B::C#method_name)
|
57
|
+
def method_identifier(subject, method)
|
58
|
+
scope = subject.is_a?(Class) ? subject.name : subject.class.name
|
59
|
+
scope + (subject.is_a?(Class) ? '.' : '#') + method.to_s
|
60
|
+
end
|
34
61
|
end # Class methods
|
35
62
|
end # Utils
|
36
63
|
end # Ribbon::Intercom
|
data/lib/ribbon/intercom.rb
CHANGED
@@ -4,10 +4,12 @@ require 'rack'
|
|
4
4
|
module Ribbon
|
5
5
|
module Intercom
|
6
6
|
require 'ribbon/intercom/railtie' if defined?(Rails)
|
7
|
-
autoload(:Service,
|
8
|
-
autoload(:Errors,
|
9
|
-
autoload(:Client,
|
10
|
-
autoload(:
|
7
|
+
autoload(:Service, 'ribbon/intercom/service')
|
8
|
+
autoload(:Errors, 'ribbon/intercom/errors')
|
9
|
+
autoload(:Client, 'ribbon/intercom/client')
|
10
|
+
autoload(:Package, 'ribbon/intercom/package')
|
11
|
+
autoload(:Packageable, 'ribbon/intercom/packageable')
|
12
|
+
autoload(:Utils, 'ribbon/intercom/utils')
|
11
13
|
|
12
14
|
module_function
|
13
15
|
|
@@ -20,10 +22,11 @@ module Ribbon
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def mock_safe
|
23
|
-
|
24
|
-
@_client = @_client.
|
25
|
+
orig_client = client
|
26
|
+
@_client = @_client.mock_safe
|
25
27
|
yield
|
26
|
-
|
28
|
+
ensure
|
29
|
+
@_client = orig_client
|
27
30
|
end
|
28
31
|
end # Intercom
|
29
32
|
end # Ribbon
|
data/lib/tasks/intercom.rake
CHANGED
@@ -4,9 +4,9 @@ require 'json'
|
|
4
4
|
|
5
5
|
namespace :intercom do
|
6
6
|
namespace :client do
|
7
|
-
task :rotate_secret, [:
|
8
|
-
|
9
|
-
puts "New #{
|
7
|
+
task :rotate_secret, [:service] => :environment do |t, args|
|
8
|
+
service = args[:service]
|
9
|
+
puts "New #{service} secret: #{Intercom[service].rotate_secret}"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ribbon-intercom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Honer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-05-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -65,20 +65,6 @@ dependencies:
|
|
65
65
|
- - ">="
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '0'
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
name: rest-client
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - "~>"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: 1.7.2
|
75
|
-
type: :runtime
|
76
|
-
prerelease: false
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - "~>"
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: 1.7.2
|
82
68
|
- !ruby/object:Gem::Dependency
|
83
69
|
name: ribbon-config
|
84
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,16 +159,30 @@ extra_rdoc_files: []
|
|
173
159
|
files:
|
174
160
|
- lib/ribbon/intercom.rb
|
175
161
|
- lib/ribbon/intercom/client.rb
|
162
|
+
- lib/ribbon/intercom/client/mock_sdk.rb
|
176
163
|
- lib/ribbon/intercom/client/sdk.rb
|
177
|
-
- lib/ribbon/intercom/client/sdk/
|
178
|
-
- lib/ribbon/intercom/client/sdk/
|
164
|
+
- lib/ribbon/intercom/client/sdk/adapters.rb
|
165
|
+
- lib/ribbon/intercom/client/sdk/adapters/adapter.rb
|
166
|
+
- lib/ribbon/intercom/client/sdk/adapters/adapter/response.rb
|
167
|
+
- lib/ribbon/intercom/client/sdk/adapters/http_adapter.rb
|
168
|
+
- lib/ribbon/intercom/client/sdk/adapters/http_adapter/connection.rb
|
169
|
+
- lib/ribbon/intercom/client/sdk/adapters/local_adapter.rb
|
170
|
+
- lib/ribbon/intercom/client/sdk/adapters/mock_adapter.rb
|
179
171
|
- lib/ribbon/intercom/errors.rb
|
172
|
+
- lib/ribbon/intercom/package.rb
|
173
|
+
- lib/ribbon/intercom/packageable.rb
|
174
|
+
- lib/ribbon/intercom/packageable/mixin.rb
|
180
175
|
- lib/ribbon/intercom/railtie.rb
|
181
176
|
- lib/ribbon/intercom/service.rb
|
182
177
|
- lib/ribbon/intercom/service/channel.rb
|
183
|
-
- lib/ribbon/intercom/service/
|
184
|
-
- lib/ribbon/intercom/service/
|
178
|
+
- lib/ribbon/intercom/service/channel/stores.rb
|
179
|
+
- lib/ribbon/intercom/service/channel/stores/mock_store.rb
|
180
|
+
- lib/ribbon/intercom/service/channel/stores/redis_store.rb
|
181
|
+
- lib/ribbon/intercom/service/channel/stores/store.rb
|
185
182
|
- lib/ribbon/intercom/utils.rb
|
183
|
+
- lib/ribbon/intercom/utils/mixins.rb
|
184
|
+
- lib/ribbon/intercom/utils/mixins/mock_safe.rb
|
185
|
+
- lib/ribbon/intercom/utils/signer.rb
|
186
186
|
- lib/ribbon/intercom/version.rb
|
187
187
|
- lib/tasks/intercom.rake
|
188
188
|
homepage: http://github.com/ribbon/intercom
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'rest-client'
|
2
|
-
|
3
|
-
module Ribbon::Intercom
|
4
|
-
class Client
|
5
|
-
class SDK
|
6
|
-
class Connection
|
7
|
-
def initialize(url, token, secret)
|
8
|
-
@client = RestClient::Resource.new(
|
9
|
-
url,
|
10
|
-
user: token,
|
11
|
-
password: secret,
|
12
|
-
) { |response, request, result| response }
|
13
|
-
end
|
14
|
-
|
15
|
-
def put(path, method_name, args)
|
16
|
-
path = _build_path(path)
|
17
|
-
headers = { "X-Intercom-Method" => method_name }
|
18
|
-
payload = _encode_args(args)
|
19
|
-
|
20
|
-
Response.new(@client[path].put(payload, headers))
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def _build_path(path)
|
26
|
-
path[0] == '/' ? path : '/' + path
|
27
|
-
end
|
28
|
-
|
29
|
-
def _encode_args(args)
|
30
|
-
Base64.strict_encode64(Marshal.dump(Utils.sanitize(args)))
|
31
|
-
end
|
32
|
-
end # Connection
|
33
|
-
end # SDK
|
34
|
-
end # Client
|
35
|
-
end # Ribbon::Intercom
|