ably 0.8.8 → 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -2
- data/LICENSE +2 -2
- data/README.md +81 -20
- data/SPEC.md +235 -178
- data/lib/ably/auth.rb +1 -1
- data/lib/ably/exceptions.rb +10 -1
- data/lib/ably/models/cipher_params.rb +114 -0
- data/lib/ably/models/connection_details.rb +8 -6
- data/lib/ably/models/error_info.rb +3 -3
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +27 -20
- data/lib/ably/models/message.rb +15 -15
- data/lib/ably/models/message_encoders/cipher.rb +8 -7
- data/lib/ably/models/presence_message.rb +17 -17
- data/lib/ably/models/protocol_message.rb +26 -19
- data/lib/ably/models/stats.rb +15 -15
- data/lib/ably/models/token_details.rb +14 -12
- data/lib/ably/models/token_request.rb +16 -14
- data/lib/ably/modules/async_wrapper.rb +1 -1
- data/lib/ably/modules/encodeable.rb +10 -10
- data/lib/ably/modules/model_common.rb +13 -5
- data/lib/ably/realtime/channel.rb +1 -2
- data/lib/ably/realtime/presence.rb +29 -58
- data/lib/ably/realtime/presence/members_map.rb +2 -2
- data/lib/ably/rest/channel.rb +1 -2
- data/lib/ably/rest/middleware/exceptions.rb +14 -4
- data/lib/ably/rest/presence.rb +3 -1
- data/lib/ably/util/crypto.rb +50 -40
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/message_spec.rb +20 -20
- data/spec/acceptance/realtime/presence_history_spec.rb +7 -7
- data/spec/acceptance/realtime/presence_spec.rb +65 -77
- data/spec/acceptance/rest/auth_spec.rb +8 -8
- data/spec/acceptance/rest/base_spec.rb +4 -4
- data/spec/acceptance/rest/channel_spec.rb +1 -1
- data/spec/acceptance/rest/client_spec.rb +1 -1
- data/spec/acceptance/rest/encoders_spec.rb +4 -4
- data/spec/acceptance/rest/message_spec.rb +15 -15
- data/spec/acceptance/rest/presence_spec.rb +4 -4
- data/spec/shared/model_behaviour.rb +7 -7
- data/spec/unit/models/cipher_params_spec.rb +140 -0
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +15 -8
- data/spec/unit/models/message_encoders/cipher_spec.rb +28 -22
- data/spec/unit/models/message_encoders/json_spec.rb +24 -0
- data/spec/unit/models/protocol_message_spec.rb +3 -3
- data/spec/unit/util/crypto_spec.rb +50 -17
- metadata +5 -2
@@ -2,7 +2,7 @@ require 'eventmachine'
|
|
2
2
|
|
3
3
|
module Ably::Modules
|
4
4
|
# Provides methods to convert synchronous operations into async operations through the use of
|
5
|
-
# {
|
5
|
+
# {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine#defer-class_method EventMachine#defer}.
|
6
6
|
# The async_wrap method can only be called from within an EventMachine reactor, and must be thread safe.
|
7
7
|
#
|
8
8
|
# @note using this AsyncWrapper should only be used for methods that are used less frequently and typically
|
@@ -5,9 +5,9 @@ module Ably::Modules
|
|
5
5
|
# Provides methods to allow this model's `data` property to be encoded and decoded based on the `encoding` property.
|
6
6
|
#
|
7
7
|
# This module expects the following:
|
8
|
-
# - A #
|
9
|
-
# - A #
|
10
|
-
# - A #raw_hash_object attribute that returns the original hash used to create this object
|
8
|
+
# - A #attributes method that returns the underlying hash object
|
9
|
+
# - A #set_attributes_object(attributes) method that updates the underlying hash object
|
10
|
+
# - A #raw_hash_object attribute that returns the original hash object used to create this object
|
11
11
|
#
|
12
12
|
module Encodeable
|
13
13
|
# Encode a message using the channel options and register encoders for the client
|
@@ -46,20 +46,20 @@ module Ably::Modules
|
|
46
46
|
|
47
47
|
def apply_encoders(method, channel)
|
48
48
|
max_encoding_length = 512
|
49
|
-
|
49
|
+
message_attributes = attributes.dup
|
50
50
|
|
51
51
|
begin
|
52
|
-
if
|
53
|
-
raise Ably::Exceptions::EncoderError("Encoding error, encoding value is too long: '#{
|
52
|
+
if message_attributes[:encoding].to_s.length > max_encoding_length
|
53
|
+
raise Ably::Exceptions::EncoderError("Encoding error, encoding value is too long: '#{message_attributes[:encoding]}'", nil, 92100)
|
54
54
|
end
|
55
55
|
|
56
|
-
previous_encoding =
|
56
|
+
previous_encoding = message_attributes[:encoding]
|
57
57
|
channel.client.encoders.each do |encoder|
|
58
|
-
encoder.send method,
|
58
|
+
encoder.send method, message_attributes, channel.options
|
59
59
|
end
|
60
|
-
end until previous_encoding ==
|
60
|
+
end until previous_encoding == message_attributes[:encoding]
|
61
61
|
|
62
|
-
|
62
|
+
set_attributes_object message_attributes
|
63
63
|
rescue Ably::Exceptions::CipherError => cipher_error
|
64
64
|
if channel.respond_to?(:emit)
|
65
65
|
channel.client.logger.error "Encoder error #{cipher_error.code} trying to #{method} message: #{cipher_error.message}"
|
@@ -12,24 +12,32 @@ module Ably::Modules
|
|
12
12
|
#
|
13
13
|
# @return [Object]
|
14
14
|
def [](key)
|
15
|
-
|
15
|
+
attributes[key]
|
16
16
|
end
|
17
17
|
|
18
18
|
def ==(other)
|
19
19
|
other.kind_of?(self.class) &&
|
20
|
-
|
20
|
+
attributes == other.attributes
|
21
21
|
end
|
22
22
|
|
23
|
-
# Return a JSON ready object from the underlying #
|
23
|
+
# Return a JSON ready object from the underlying #attributes using Ably naming conventions for keys
|
24
|
+
# @return [Hash]
|
24
25
|
def as_json
|
25
|
-
|
26
|
+
attributes.as_json.reject { |key, val| val.nil? }
|
26
27
|
end
|
27
28
|
|
28
|
-
# Stringify the JSON representation of this object from the underlying #
|
29
|
+
# Stringify the JSON representation of this object from the underlying #attributes
|
30
|
+
# @return [String]
|
29
31
|
def to_json(*args)
|
30
32
|
as_json.to_json(*args)
|
31
33
|
end
|
32
34
|
|
35
|
+
# @!attribute [r] hash
|
36
|
+
# @return [Integer] Compute a hash-code for this hash. Two hashes with the same content will have the same hash code
|
37
|
+
def hash
|
38
|
+
attributes.hash
|
39
|
+
end
|
40
|
+
|
33
41
|
private
|
34
42
|
def ensure_utf8_string_for(attribute, value)
|
35
43
|
if value
|
@@ -82,8 +82,7 @@ module Ably
|
|
82
82
|
# @param client [Ably::Rest::Client]
|
83
83
|
# @param name [String] The name of the channel
|
84
84
|
# @param channel_options [Hash] Channel options, currently reserved for Encryption options
|
85
|
-
# @option channel_options [
|
86
|
-
# @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +cipher_params+ options
|
85
|
+
# @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
|
87
86
|
#
|
88
87
|
def initialize(client, name, channel_options = {})
|
89
88
|
ensure_utf_8 :name, name
|
@@ -59,26 +59,16 @@ module Ably::Realtime
|
|
59
59
|
# Enter this client into this channel. This client will be added to the presence set
|
60
60
|
# and presence subscribers will see an enter message for this client.
|
61
61
|
#
|
62
|
-
# @param [Hash]
|
63
|
-
# @option options [String] :data optional data (eg a status message) for this member
|
64
|
-
# @option options [String] :client_id the optional id of the client.
|
65
|
-
# This option is provided to support connections from server instances that act on behalf of
|
66
|
-
# multiple client_ids. In order to be able to enter the channel with this method, the client
|
67
|
-
# library must have been instanced either with a key, or with a token bound to the wildcard clientId.
|
62
|
+
# @param [String,Hash,nil] data optional data (eg a status message) for this member
|
68
63
|
#
|
69
64
|
# @yield [Ably::Realtime::Presence] On success, will call the block with this {Ably::Realtime::Presence} object
|
70
65
|
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
71
66
|
#
|
72
|
-
def enter(
|
73
|
-
client_id = options.fetch(:client_id, self.client_id)
|
74
|
-
data = options.fetch(:data, nil)
|
67
|
+
def enter(data = nil, &success_block)
|
75
68
|
deferrable = create_deferrable
|
76
69
|
|
77
|
-
|
78
|
-
ensure_supported_payload data unless data.nil?
|
79
|
-
|
70
|
+
ensure_supported_payload data
|
80
71
|
@data = data
|
81
|
-
@client_id = client_id
|
82
72
|
|
83
73
|
return deferrable_succeed(deferrable, &success_block) if state == STATE.Entered
|
84
74
|
|
@@ -94,8 +84,8 @@ module Ably::Realtime
|
|
94
84
|
Ably::Models::PresenceMessage::ACTION.Enter,
|
95
85
|
deferrable: deferrable,
|
96
86
|
target_state: STATE.Entered,
|
97
|
-
client_id: client_id,
|
98
87
|
data: data,
|
88
|
+
client_id: client_id,
|
99
89
|
failed_state: STATE.Failed,
|
100
90
|
&success_block
|
101
91
|
)
|
@@ -111,36 +101,30 @@ module Ably::Realtime
|
|
111
101
|
# either with a key, or with a token bound to the wildcard client_id
|
112
102
|
#
|
113
103
|
# @param [String] client_id id of the client
|
114
|
-
#
|
115
|
-
# @param [Hash] options an options Hash for this client event
|
116
|
-
# @option options [String] :data optional data (eg a status message) for this member
|
104
|
+
# @param [String,Hash,nil] data optional data (eg a status message) for this member
|
117
105
|
#
|
118
106
|
# @yield [Ably::Realtime::Presence] On success, will call the block with this {Ably::Realtime::Presence} object
|
119
107
|
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
120
108
|
#
|
121
|
-
def enter_client(client_id,
|
122
|
-
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
109
|
+
def enter_client(client_id, data = nil, &success_block)
|
123
110
|
ensure_supported_client_id client_id
|
124
|
-
ensure_supported_payload
|
111
|
+
ensure_supported_payload data
|
125
112
|
|
126
|
-
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Enter, client_id,
|
113
|
+
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Enter, client_id, data, &success_block)
|
127
114
|
end
|
128
115
|
|
129
116
|
# Leave this client from this channel. This client will be removed from the presence
|
130
117
|
# set and presence subscribers will see a leave message for this client.
|
131
118
|
#
|
132
|
-
# @param
|
133
|
-
# @option options [String] :data optional data (eg a status message) for this member
|
119
|
+
# @param (see Presence#enter)
|
134
120
|
#
|
135
121
|
# @yield (see Presence#enter)
|
136
122
|
# @return (see Presence#enter)
|
137
123
|
#
|
138
|
-
def leave(
|
139
|
-
data = options.fetch(:data, self.data) # nil value defaults leave data to existing value
|
124
|
+
def leave(data = nil, &success_block)
|
140
125
|
deferrable = create_deferrable
|
141
126
|
|
142
|
-
|
143
|
-
ensure_supported_payload data unless data.nil?
|
127
|
+
ensure_supported_payload data
|
144
128
|
raise Ably::Exceptions::Standard.new('Unable to leave presence channel that is not entered', 400, 91002) unless able_to_leave?
|
145
129
|
|
146
130
|
@data = data
|
@@ -159,8 +143,8 @@ module Ably::Realtime
|
|
159
143
|
Ably::Models::PresenceMessage::ACTION.Leave,
|
160
144
|
deferrable: deferrable,
|
161
145
|
target_state: STATE.Left,
|
162
|
-
client_id: client_id,
|
163
146
|
data: data,
|
147
|
+
client_id: client_id,
|
164
148
|
failed_state: STATE.Failed,
|
165
149
|
&success_block
|
166
150
|
)
|
@@ -172,35 +156,30 @@ module Ably::Realtime
|
|
172
156
|
# presence set and presence subscribers will see a leave message for this client.
|
173
157
|
#
|
174
158
|
# @param (see Presence#enter_client)
|
175
|
-
# @option options (see Presence#enter_client)
|
176
159
|
#
|
177
160
|
# @yield (see Presence#enter_client)
|
178
161
|
# @return (see Presence#enter_client)
|
179
162
|
#
|
180
|
-
def leave_client(client_id,
|
181
|
-
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
163
|
+
def leave_client(client_id, data = nil, &success_block)
|
182
164
|
ensure_supported_client_id client_id
|
183
|
-
ensure_supported_payload
|
165
|
+
ensure_supported_payload data
|
184
166
|
|
185
|
-
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Leave, client_id,
|
167
|
+
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Leave, client_id, data, &success_block)
|
186
168
|
end
|
187
169
|
|
188
170
|
# Update the presence data for this client. If the client is not already a member of
|
189
171
|
# the presence set it will be added, and presence subscribers will see an enter or
|
190
172
|
# update message for this client.
|
191
173
|
#
|
192
|
-
# @param
|
193
|
-
# @option options [String] :data optional data (eg a status message) for this member
|
174
|
+
# @param (see Presence#enter)
|
194
175
|
#
|
195
176
|
# @yield (see Presence#enter)
|
196
177
|
# @return (see Presence#enter)
|
197
178
|
#
|
198
|
-
def update(
|
199
|
-
data = options.fetch(:data, nil)
|
179
|
+
def update(data = nil, &success_block)
|
200
180
|
deferrable = create_deferrable
|
201
181
|
|
202
|
-
|
203
|
-
ensure_supported_payload data unless data.nil?
|
182
|
+
ensure_supported_payload data
|
204
183
|
|
205
184
|
@data = data
|
206
185
|
|
@@ -224,17 +203,15 @@ module Ably::Realtime
|
|
224
203
|
# enables it to represent an arbitrary clientId.
|
225
204
|
#
|
226
205
|
# @param (see Presence#enter_client)
|
227
|
-
# @option options (see Presence#enter_client)
|
228
206
|
#
|
229
207
|
# @yield (see Presence#enter_client)
|
230
208
|
# @return (see Presence#enter_client)
|
231
209
|
#
|
232
|
-
def update_client(client_id,
|
233
|
-
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
210
|
+
def update_client(client_id, data = nil, &success_block)
|
234
211
|
ensure_supported_client_id client_id
|
235
|
-
ensure_supported_payload
|
212
|
+
ensure_supported_payload data
|
236
213
|
|
237
|
-
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Update, client_id,
|
214
|
+
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Update, client_id, data, &success_block)
|
238
215
|
end
|
239
216
|
|
240
217
|
# Get the presence state for this Channel.
|
@@ -344,8 +321,8 @@ module Ably::Realtime
|
|
344
321
|
end
|
345
322
|
|
346
323
|
# @return [Ably::Models::PresenceMessage] presence message is returned allowing callbacks to be added
|
347
|
-
def send_presence_protocol_message(presence_action, client_id,
|
348
|
-
presence_message = create_presence_message(presence_action, client_id,
|
324
|
+
def send_presence_protocol_message(presence_action, client_id, data)
|
325
|
+
presence_message = create_presence_message(presence_action, client_id, data)
|
349
326
|
unless presence_message.client_id
|
350
327
|
raise Ably::Exceptions::Standard.new('Unable to enter create presence message without a client_id', 400, 91000)
|
351
328
|
end
|
@@ -361,12 +338,12 @@ module Ably::Realtime
|
|
361
338
|
presence_message
|
362
339
|
end
|
363
340
|
|
364
|
-
def create_presence_message(action, client_id,
|
341
|
+
def create_presence_message(action, client_id, data)
|
365
342
|
model = {
|
366
343
|
action: Ably::Models::PresenceMessage.ACTION(action).to_i,
|
367
|
-
clientId: client_id
|
344
|
+
clientId: client_id,
|
345
|
+
data: data
|
368
346
|
}
|
369
|
-
model.merge!(data: options.fetch(:data)) if options.has_key?(:data)
|
370
347
|
|
371
348
|
Ably::Models::PresenceMessage.new(model, logger: logger).tap do |presence_message|
|
372
349
|
presence_message.encode self.channel
|
@@ -406,13 +383,7 @@ module Ably::Realtime
|
|
406
383
|
target_state = options.fetch(:target_state, nil)
|
407
384
|
failed_state = options.fetch(:failed_state, nil)
|
408
385
|
|
409
|
-
|
410
|
-
{ data: options.fetch(:data) }
|
411
|
-
else
|
412
|
-
{ }
|
413
|
-
end
|
414
|
-
|
415
|
-
send_presence_protocol_message(action, client_id, protocol_message_options).tap do |protocol_message|
|
386
|
+
send_presence_protocol_message(action, client_id, options[:data]).tap do |protocol_message|
|
416
387
|
protocol_message.callback do |message|
|
417
388
|
change_state target_state, message if target_state
|
418
389
|
deferrable_succeed deferrable, &success_block
|
@@ -437,12 +408,12 @@ module Ably::Realtime
|
|
437
408
|
deferrable
|
438
409
|
end
|
439
410
|
|
440
|
-
def send_presence_action_for_client(action, client_id,
|
411
|
+
def send_presence_action_for_client(action, client_id, data, &success_block)
|
441
412
|
ensure_presence_publishable_on_connection
|
442
413
|
|
443
414
|
deferrable = create_deferrable
|
444
415
|
ensure_channel_attached(deferrable) do
|
445
|
-
send_presence_protocol_message(action, client_id,
|
416
|
+
send_presence_protocol_message(action, client_id, data).tap do |protocol_message|
|
446
417
|
protocol_message.callback { |message| deferrable_succeed deferrable, &success_block }
|
447
418
|
protocol_message.errback { |error| deferrable_fail deferrable, error }
|
448
419
|
end
|
@@ -60,8 +60,8 @@ module Ably::Realtime
|
|
60
60
|
# Get the list of presence members
|
61
61
|
#
|
62
62
|
# @param [Hash,String] options an options Hash to filter members
|
63
|
-
# @option options [String] :client_id optional client_id for the member
|
64
|
-
# @option options [String] :connection_id optional connection_id for the member
|
63
|
+
# @option options [String] :client_id optional client_id filter for the member
|
64
|
+
# @option options [String] :connection_id optional connection_id filter for the member
|
65
65
|
# @option options [String] :wait_for_sync defaults to false, if true the get method waits for the initial presence sync following channel attachment to complete before returning the members present
|
66
66
|
#
|
67
67
|
# @yield [Array<Ably::Models::PresenceMessage>] array of present members
|
data/lib/ably/rest/channel.rb
CHANGED
@@ -19,8 +19,7 @@ module Ably
|
|
19
19
|
# @param client [Ably::Rest::Client]
|
20
20
|
# @param name [String] The name of the channel
|
21
21
|
# @param channel_options [Hash] Channel options, currently reserved for Encryption options
|
22
|
-
# @option channel_options [
|
23
|
-
# @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +cipher_params+ options
|
22
|
+
# @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
|
24
23
|
#
|
25
24
|
def initialize(client, name, channel_options = {})
|
26
25
|
ensure_utf_8 :name, name
|
@@ -30,12 +30,22 @@ module Ably
|
|
30
30
|
|
31
31
|
message = 'Unknown server error' if message.to_s.strip == ''
|
32
32
|
|
33
|
+
exception_args = [message, error_status_code, error_code]
|
34
|
+
|
33
35
|
if env.status >= 500
|
34
|
-
raise Ably::Exceptions::ServerError.new(
|
35
|
-
elsif env.status == 401
|
36
|
-
|
36
|
+
raise Ably::Exceptions::ServerError.new(*exception_args)
|
37
|
+
elsif env.status == 401
|
38
|
+
if TOKEN_EXPIRED_CODE.include?(error_code)
|
39
|
+
raise Ably::Exceptions::TokenExpired.new(*exception_args)
|
40
|
+
else
|
41
|
+
raise Ably::Exceptions::UnauthorizedRequest.new(*exception_args)
|
42
|
+
end
|
43
|
+
elsif env.status == 403
|
44
|
+
raise Ably::Exceptions::ForbiddenRequest.new(*exception_args)
|
45
|
+
elsif env.status == 404
|
46
|
+
raise Ably::Exceptions::ResourceMissing.new(*exception_args)
|
37
47
|
else
|
38
|
-
raise Ably::Exceptions::InvalidRequest.new(
|
48
|
+
raise Ably::Exceptions::InvalidRequest.new(*exception_args)
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
data/lib/ably/rest/presence.rb
CHANGED
@@ -23,7 +23,9 @@ module Ably
|
|
23
23
|
# Obtain the set of members currently present for a channel
|
24
24
|
#
|
25
25
|
# @param [Hash] options the options for the set of members present
|
26
|
-
# @option options [Integer]
|
26
|
+
# @option options [Integer] :limit Maximum number of members to retrieve up to 1,000, defaults to 100
|
27
|
+
# @option options [String] :client_id optional client_id filter for the member
|
28
|
+
# @option options [String] :connection_id optional connection_id filter for the member
|
27
29
|
#
|
28
30
|
# @return [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResult page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResult#items #items}.
|
29
31
|
#
|
data/lib/ably/util/crypto.rb
CHANGED
@@ -4,52 +4,65 @@ require 'openssl'
|
|
4
4
|
module Ably::Util
|
5
5
|
class Crypto
|
6
6
|
DEFAULTS = {
|
7
|
-
algorithm: '
|
8
|
-
mode: '
|
9
|
-
key_length:
|
7
|
+
algorithm: 'aes',
|
8
|
+
mode: 'cbc',
|
9
|
+
key_length: 256,
|
10
10
|
}
|
11
11
|
|
12
12
|
BLOCK_LENGTH = 16
|
13
13
|
|
14
|
-
# Configured
|
14
|
+
# Configured {Ably::Models::CipherParams} for this Crypto object, see {#initialize} for a list of configureable options
|
15
15
|
#
|
16
|
-
# @return [
|
17
|
-
attr_reader :
|
16
|
+
# @return [Ably::Models::CipherParams]
|
17
|
+
attr_reader :cipher_params
|
18
18
|
|
19
19
|
# Creates a {Ably::Util::Crypto} object
|
20
20
|
#
|
21
|
-
# @param [Hash]
|
22
|
-
# @option
|
23
|
-
# @option options [String] :algorithm optional (default AES), specify the encryption algorithm supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
24
|
-
# @option options [String] :mode optional (default CBC), specify the cipher mode supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
25
|
-
# @option options [Integer] :key_length optional (default 128), specify the key length of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
26
|
-
# @option options [String] :combined optional (default AES-128-CBC), specify in one option the algorithm, key length and cipher of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
21
|
+
# @param [Hash] params a Hash used to configure the Crypto library's {Ably::Models::CipherParams}
|
22
|
+
# @option params (see Ably::Models::CipherParams#initialize)
|
27
23
|
#
|
28
24
|
# @return [Ably::Util::Crypto]
|
29
25
|
#
|
30
26
|
# @example
|
31
|
-
#
|
27
|
+
# key = Ably::Util::Crypto.generate_random_key
|
28
|
+
# crypto = Ably::Util::Crypto.new(key: key)
|
32
29
|
# encrypted = crypto.encrypt('secret text')
|
33
30
|
# crypto.decrypt(decrypted) # => 'secret text'
|
34
31
|
#
|
35
|
-
def initialize(
|
36
|
-
|
37
|
-
@
|
32
|
+
def initialize(params)
|
33
|
+
@fixed_iv = params.delete(:fixed_iv) if params.kind_of?(Hash)
|
34
|
+
@cipher_params = Ably::Models::CipherParams(params)
|
38
35
|
end
|
39
36
|
|
40
|
-
# Obtain a default CipherParams. This uses default algorithm, mode and
|
41
|
-
# padding and key length.
|
42
|
-
# system SecureRandom; the key may be obtained from the returned CipherParams
|
37
|
+
# Obtain a default {Ably::Models::CipherParams}. This uses default algorithm, mode and
|
38
|
+
# padding and key length. An IV is generated using the default
|
39
|
+
# system SecureRandom; the key may be obtained from the returned {Ably::Models::CipherParams}
|
43
40
|
# for out-of-band distribution to other clients.
|
41
|
+
|
42
|
+
# @param [Hash] params a Hash used to configure the Crypto library's {Ably::Models::CipherParams}
|
43
|
+
# @option params (see Ably::Models::CipherParams#initialize)
|
44
|
+
#
|
45
|
+
# @return [Ably::Models::CipherParams] Configured cipher params with :key, :algorithm, :mode, :key_length attributes
|
46
|
+
#
|
47
|
+
def self.get_default_params(params = {})
|
48
|
+
Ably::Models::CipherParams(params)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Generate a random encryption key from the supplied keylength (or the
|
52
|
+
# default key_length of 256 if none supplied)
|
44
53
|
#
|
45
|
-
# @
|
54
|
+
# @param [Integer] key_length Optional (default 256) key length for the generated random key. 128 and 256 bit key lengths are supported
|
55
|
+
# @return Binary String (byte array) with ASCII_8BIT encoding
|
46
56
|
#
|
47
|
-
def self.
|
48
|
-
params = DEFAULTS.merge(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
def self.generate_random_key(key_length = DEFAULTS.fetch(:key_length))
|
58
|
+
params = DEFAULTS.merge(key_length: key_length)
|
59
|
+
OpenSSL::Cipher.new(cipher_type(params)).random_key
|
60
|
+
end
|
61
|
+
|
62
|
+
# The Cipher algorithm string such as AES-128-CBC
|
63
|
+
# @api private
|
64
|
+
def self.cipher_type(options)
|
65
|
+
Ably::Models::CipherParams.cipher_type(options)
|
53
66
|
end
|
54
67
|
|
55
68
|
# Encrypt payload using configured Cipher
|
@@ -58,13 +71,13 @@ module Ably::Util
|
|
58
71
|
# @param [Hash] encrypt_options an options Hash to configure the encrypt action
|
59
72
|
# @option encrypt_options [String] :iv optionally use the provided Initialization Vector instead of a randomly generated IV
|
60
73
|
#
|
61
|
-
# @return [String] binary string with
|
74
|
+
# @return [String] binary string with +Encoding::ASCII_8BIT+ encoding
|
62
75
|
#
|
63
76
|
def encrypt(payload, encrypt_options = {})
|
64
77
|
cipher = openssl_cipher
|
65
78
|
cipher.encrypt
|
66
79
|
cipher.key = key
|
67
|
-
iv = encrypt_options[:iv] ||
|
80
|
+
iv = encrypt_options[:iv] || fixed_iv || cipher.random_iv
|
68
81
|
cipher.iv = iv
|
69
82
|
|
70
83
|
iv << cipher.update(payload) << cipher.final
|
@@ -90,31 +103,28 @@ module Ably::Util
|
|
90
103
|
decipher.update(encrypted_payload) << decipher.final
|
91
104
|
end
|
92
105
|
|
93
|
-
# Generate a random key
|
94
|
-
# @return [String]
|
95
|
-
def random_key
|
96
|
-
openssl_cipher.random_key
|
97
|
-
end
|
98
|
-
|
99
106
|
# Generate a random IV
|
100
107
|
# @return [String]
|
101
108
|
def random_iv
|
102
109
|
openssl_cipher.random_iv
|
103
110
|
end
|
104
111
|
|
105
|
-
|
112
|
+
private
|
113
|
+
# Used solely for tests to fix the IV instead of randomly generate one
|
114
|
+
attr_reader :fixed_iv
|
115
|
+
|
116
|
+
# Generate a random key
|
106
117
|
# @return [String]
|
107
|
-
def
|
108
|
-
|
118
|
+
def random_key
|
119
|
+
openssl_cipher.random_key
|
109
120
|
end
|
110
121
|
|
111
|
-
private
|
112
122
|
def key
|
113
|
-
|
123
|
+
cipher_params.key
|
114
124
|
end
|
115
125
|
|
116
126
|
def openssl_cipher
|
117
|
-
OpenSSL::Cipher.new(cipher_type)
|
127
|
+
@openssl_cipher ||= OpenSSL::Cipher.new(cipher_params.cipher_type)
|
118
128
|
end
|
119
129
|
end
|
120
130
|
end
|