ably 0.8.8 → 0.8.9
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/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
|