ribbon-intercom 0.2.3 → 0.3.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.
- 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
|