ably-rest 0.7.5 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +2 -0
  3. data/README.md +41 -15
  4. data/SPEC.md +654 -518
  5. data/lib/submodules/ably-ruby/.gitignore +1 -0
  6. data/lib/submodules/ably-ruby/.gitmodules +3 -0
  7. data/lib/submodules/ably-ruby/README.md +54 -26
  8. data/lib/submodules/ably-ruby/SPEC.md +468 -322
  9. data/lib/submodules/ably-ruby/ably.gemspec +4 -2
  10. data/lib/submodules/ably-ruby/lib/ably/auth.rb +185 -131
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +31 -44
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +2 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +1 -2
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +67 -24
  16. data/lib/submodules/ably-ruby/lib/ably/models/stats_types.rb +131 -0
  17. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +101 -0
  18. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +108 -0
  19. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +3 -2
  20. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +1 -1
  21. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +2 -2
  22. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +3 -7
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +32 -5
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +1 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -8
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +12 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest.rb +3 -7
  29. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +13 -10
  30. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +19 -20
  31. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +14 -12
  32. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +3 -3
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +18 -18
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +5 -5
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +12 -12
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +8 -8
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +262 -158
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +11 -9
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +28 -21
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +1 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +30 -27
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +1 -1
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +10 -10
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +93 -56
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +50 -45
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
  53. data/lib/submodules/ably-ruby/spec/rspec_config.rb +3 -2
  54. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +36 -28
  55. data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -0
  56. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +3 -3
  57. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
  58. data/lib/submodules/ably-ruby/spec/support/test_app.rb +20 -33
  59. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +18 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +81 -72
  61. data/lib/submodules/ably-ruby/spec/unit/models/stats_spec.rb +289 -0
  62. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +111 -0
  63. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +110 -0
  64. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  65. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  66. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +1 -1
  67. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +1 -1
  68. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +8 -8
  69. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +1 -1
  70. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +1 -1
  71. metadata +9 -7
  72. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +0 -74
  73. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +0 -56
  74. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +0 -56
  75. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +0 -113
  76. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +0 -86
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Ably::VERSION
9
9
  spec.authors = ['Lewis Marshall', "Matthew O'Riordan"]
10
10
  spec.email = ['lewis@lmars.net', 'matt@ably.io']
11
- spec.description = %q{A Ruby client library for ably.io, the real-time messaging service}
12
- spec.summary = %q{A Ruby client library for ably.io, the real-time messaging service}
11
+ spec.description = %q{A Ruby client library for ably.io, the realtime messaging service}
12
+ spec.summary = %q{A Ruby client library for ably.io, the realtime messaging service}
13
13
  spec.homepage = 'http://github.com/ably/ably-ruby'
14
14
  spec.license = 'MIT'
15
15
 
@@ -33,4 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency 'rspec-retry'
34
34
  spec.add_development_dependency 'yard'
35
35
  spec.add_development_dependency 'webmock'
36
+
37
+ spec.add_development_dependency 'coveralls'
36
38
  end
@@ -5,20 +5,20 @@ require 'securerandom'
5
5
  require 'ably/rest/middleware/external_exceptions'
6
6
 
7
7
  module Ably
8
- # Auth is responsible for authentication with {https://ably.io Ably} using basic or token authentication
8
+ # Auth is responsible for authentication with {https://www.ably.io Ably} using basic or token authentication
9
9
  #
10
- # Find out more about Ably authentication at: http://docs.ably.io/other/authentication/
10
+ # Find out more about Ably authentication at: https://www.ably.io/documentation/general/authentication/
11
11
  #
12
12
  # @!attribute [r] client_id
13
13
  # @return [String] The provided client ID, used for identifying this client for presence purposes
14
- # @!attribute [r] current_token
15
- # @return [Ably::Models::Token] Current {Ably::Models::Token} issued by this library or one of the provided callbacks used to authenticate requests
16
- # @!attribute [r] token_id
17
- # @return [String] Token ID provided to the {Ably::Client} constructor that is used to authenticate all requests
18
- # @!attribute [r] api_key
19
- # @return [String] Complete API key containing both the key ID and key secret, if present
20
- # @!attribute [r] key_id
21
- # @return [String] Key ID (public part of the API key), if present
14
+ # @!attribute [r] current_token_details
15
+ # @return [Ably::Models::TokenDetails] Current {Ably::Models::TokenDetails} issued by this library or one of the provided callbacks used to authenticate requests
16
+ # @!attribute [r] token
17
+ # @return [String] Token string provided to the {Ably::Client} constructor that is used to authenticate all requests
18
+ # @!attribute [r] key
19
+ # @return [String] Complete API key containing both the key name and key secret, if present
20
+ # @!attribute [r] key_name
21
+ # @return [String] Key name (public part of the API key), if present
22
22
  # @!attribute [r] key_secret
23
23
  # @return [String] Key secret (private secure part of the API key), if present
24
24
  # @!attribute [r] options
@@ -28,7 +28,13 @@ module Ably
28
28
  include Ably::Modules::Conversions
29
29
  include Ably::Modules::HttpHelpers
30
30
 
31
- attr_reader :options, :current_token
31
+ # Default capability Hash object and TTL in seconds for issued tokens
32
+ TOKEN_DEFAULTS = {
33
+ capability: { '*' => ['*'] },
34
+ ttl: 60 * 60 # 1 hour in seconds
35
+ }
36
+
37
+ attr_reader :options, :current_token_details
32
38
  alias_method :auth_options, :options
33
39
 
34
40
  # Creates an Auth object
@@ -36,31 +42,31 @@ module Ably
36
42
  # @param [Ably::Rest::Client] client {Ably::Rest::Client} this Auth object uses
37
43
  # @param [Hash] options (see Ably::Rest::Client#initialize)
38
44
  # @option (see Ably::Rest::Client#initialize)
39
- # @yield (see Ably::Rest::Client#initialize)
40
45
  #
41
- def initialize(client, options, &token_request_block)
46
+ def initialize(client, options)
47
+ ensure_valid_auth_attributes options
48
+
42
49
  auth_options = options.dup
43
50
 
44
51
  @client = client
45
52
  @options = auth_options
46
- @default_token_block = token_request_block if block_given?
47
53
 
48
54
  unless auth_options.kind_of?(Hash)
49
55
  raise ArgumentError, 'Expected auth_options to be a Hash'
50
56
  end
51
57
 
52
- if auth_options[:api_key] && (auth_options[:key_secret] || auth_options[:key_id])
53
- raise ArgumentError, 'api_key and key_id or key_secret are mutually exclusive. Provider either an api_key or key_id & key_secret'
58
+ if auth_options[:key] && (auth_options[:key_secret] || auth_options[:key_name])
59
+ raise ArgumentError, 'key and key_name or key_secret are mutually exclusive. Provider either a key or key_name & key_secret'
54
60
  end
55
61
 
56
- split_api_key_into_key_and_secret! auth_options if auth_options[:api_key]
62
+ split_api_key_into_key_and_secret! auth_options if auth_options[:key]
57
63
 
58
64
  if using_basic_auth? && !api_key_present?
59
- raise ArgumentError, 'api_key is missing. Either an API key, token, or token auth method must be provided'
65
+ raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
60
66
  end
61
67
 
62
68
  if has_client_id?
63
- raise ArgumentError, 'client_id cannot be provided without a complete API key. Key ID & Secret is needed to authenticate with Ably and obtain a token' unless api_key_present?
69
+ raise ArgumentError, 'client_id cannot be provided without a complete API key. Key name & Secret is needed to authenticate with Ably and obtain a token' unless api_key_present?
64
70
  ensure_utf_8 :client_id, client_id
65
71
  end
66
72
 
@@ -73,18 +79,14 @@ module Ably
73
79
  #
74
80
  # @param [Hash] options the options for the token request
75
81
  # @option options (see #request_token)
76
- # @option options [String] :api_key API key comprising the key ID and key secret in a single string
77
- # @option options [Boolean] :force obtains a new token even if the current token is valid
78
- #
79
- # @yield (see #request_token)
80
- # @yieldparam [Hash] options options passed to {#authorise} will be in turn sent to the block in this argument
81
- # @yieldreturn (see #request_token)
82
+ # @option options [String] :key API key comprising the key name and key secret in a single string
83
+ # @option options [Boolean] :force obtains a new token even if the current token is valid
82
84
  #
83
85
  # @return (see #request_token)
84
86
  #
85
87
  # @example
86
88
  # # will issue a simple token request using basic auth
87
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
89
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
88
90
  # token = client.auth.authorise
89
91
  #
90
92
  # # will use token request from block to authorise if not already authorised
@@ -93,45 +95,42 @@ module Ably
93
95
  # token_request
94
96
  # end
95
97
  #
96
- def authorise(options = {}, &token_request_block)
97
- if !options[:force] && current_token
98
- return current_token unless current_token.expired?
98
+ def authorise(options = {})
99
+ ensure_valid_auth_attributes options
100
+
101
+ if current_token_details && !options[:force]
102
+ return current_token_details unless current_token_details.expired?
99
103
  end
100
104
 
101
105
  options = options.clone
102
- split_api_key_into_key_and_secret! options if options[:api_key]
103
-
104
- @options = @options.merge(options)
105
- @default_token_block = token_request_block if block_given?
106
+ split_api_key_into_key_and_secret! options if options[:key]
107
+ @options = @options.merge(options) # update default options
106
108
 
107
- @current_token = request_token(options, &token_request_block)
109
+ @current_token_details = request_token(options)
108
110
  end
109
111
 
110
- # Request a {Ably::Models::Token} which can be used to make authenticated token based requests
112
+ # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
111
113
  #
112
114
  # @param [Hash] options the options for the token request
113
- # @option options [String] :key_id key ID for the designated application (defaults to client key_id)
114
- # @option options [String] :key_secret key secret for the designated application used to sign token requests (defaults to client key_secret)
115
- # @option options [String] :client_id client ID identifying this connection to other clients (defaults to client client_id if configured)
116
- # @option options [String] :auth_url a URL to be used to GET or POST a set of token request params, to obtain a signed token request.
117
- # @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
118
- # @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
119
- # @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
120
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
121
- # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
122
- # @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
123
- # @option options [Time] :timestamp the time of the of the request
124
- # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
115
+ # @option options [String] :key complete API key for the designated application
116
+ # @option options [String] :client_id client ID identifying this connection to other clients (defaults to client client_id if configured)
117
+ # @option options [String] :auth_url a URL to be used to GET or POST a set of token request params, to obtain a signed token request.
118
+ # @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
119
+ # @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
120
+ # @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
121
+ # @option options [Proc] :auth_callback this Proc / block will be called with the {#auth_options Auth#auth_options Hash} as the first argument whenever a new token is required.
122
+ # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
123
+ # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
124
+ # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
125
+ # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
126
+ # @option options [Time] :timestamp the time of the of the request
127
+ # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
125
128
  #
126
- # @yield [options] (optional) if a token request block is passed to this method, then this block will be called whenever a new token is required
127
- # @yieldparam [Hash] options options passed to {#request_token} will be in turn sent to the block in this argument
128
- # @yieldreturn [Hash] expects a valid token request object, see {#create_token_request}
129
- #
130
- # @return [Ably::Models::Token]
129
+ # @return [Ably::Models::TokenDetails]
131
130
  #
132
131
  # @example
133
132
  # # simple token request using basic auth
134
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
133
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
135
134
  # token = client.auth.request_token
136
135
  #
137
136
  # # token request using auth block
@@ -141,96 +140,104 @@ module Ably
141
140
  # end
142
141
  #
143
142
  def request_token(options = {})
144
- token_options = self.auth_options.merge(options)
145
-
146
- auth_url = token_options.delete(:auth_url)
147
- token_request = if block_given?
148
- yield token_options
149
- elsif default_token_block
150
- default_token_block.call(token_options)
151
- elsif auth_url
143
+ ensure_valid_auth_attributes options
144
+
145
+ token_options = auth_options.merge(options)
146
+
147
+ token_request = if auth_callback = token_options.delete(:auth_callback)
148
+ auth_callback.call(token_options)
149
+ elsif auth_url = token_options.delete(:auth_url)
152
150
  token_request_from_auth_url(auth_url, token_options)
153
151
  else
154
152
  create_token_request(token_options)
155
153
  end
156
154
 
157
- token_request = IdiomaticRubyWrapper(token_request)
158
-
159
- if token_request.has_key?(:issued_at) && token_request.has_key?(:expires)
160
- Ably::Models::Token.new(token_request)
161
- else
162
- response = client.post("/keys/#{token_request.fetch(:id)}/requestToken", token_request.hash, send_auth_header: false, disable_automatic_reauthorise: true)
163
- body = IdiomaticRubyWrapper(response.body)
164
- Ably::Models::Token.new(body.fetch(:access_token))
155
+ case token_request
156
+ when Ably::Models::TokenDetails
157
+ return token_request
158
+ when Hash
159
+ return Ably::Models::TokenDetails.new(token_request) if IdiomaticRubyWrapper(token_request).has_key?(:issued)
160
+ when String
161
+ return Ably::Models::TokenDetails.new(token: token_request)
165
162
  end
163
+
164
+ token_request = Ably::Models::TokenRequest(token_request)
165
+
166
+ response = client.post("/keys/#{token_request.key_name}/requestToken",
167
+ token_request.hash, send_auth_header: false,
168
+ disable_automatic_reauthorise: true)
169
+
170
+ Ably::Models::TokenDetails.new(response.body)
166
171
  end
167
172
 
168
173
  # Creates and signs a token request that can then subsequently be used by any client to request a token
169
174
  #
170
175
  # @param [Hash] options the options for the token request
171
- # @option options [String] :key_id key ID for the designated application
172
- # @option options [String] :key_secret key secret for the designated application used to sign token requests (defaults to client key_secret)
176
+ # @option options [String] :key complete API key for the designated application
173
177
  # @option options [String] :client_id client ID identifying this connection to other clients
174
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
178
+ # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
175
179
  # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
176
- # @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
180
+ # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
177
181
  # @option options [Time] :timestamp the time of the of the request
178
182
  # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
179
- # @return [Hash]
183
+ #
184
+ # @return [Models::TokenRequest]
180
185
  #
181
186
  # @example
182
- # client.auth.create_request_token(id: 'asd.asd', ttl: 3600)
183
- # # => {
187
+ # client.auth.create_token_request(id: 'asd.asd', ttl: 3600)
188
+ # #<Ably::Models::TokenRequest:0x007fd5d919df78
189
+ # # @hash={
184
190
  # # :id=>"asds.adsa",
185
- # # :client_id=>nil,
186
- # # :ttl=>3600,
187
- # # :timestamp=>1410718527,
191
+ # # :clientId=>nil,
192
+ # # :ttl=>3600000,
193
+ # # :timestamp=>1428973674000,
188
194
  # # :capability=>"{\"*\":[\"*\"]}",
189
195
  # # :nonce=>"95e543b88299f6bae83df9b12fbd1ecd",
190
- # # :mac=>"881oZHeFo6oMim7N64y2vFHtSlpQ2gn/uE56a8gUxHw="
191
- # # }
196
+ # # :mac=>"881oZHeFo6oMim7....uE56a8gUxHw="
197
+ # # }
198
+ # #>>
192
199
  def create_token_request(options = {})
193
- token_attributes = %w(id client_id ttl timestamp capability nonce persisted)
200
+ ensure_valid_auth_attributes options
194
201
 
195
- token_options = options.clone
202
+ token_options = options.clone
196
203
 
197
- request_key_id = token_options.delete(:key_id) || key_id
204
+ split_api_key_into_key_and_secret! token_options if token_options[:key]
205
+ request_key_name = token_options.delete(:key_name) || key_name
198
206
  request_key_secret = token_options.delete(:key_secret) || key_secret
199
-
200
- raise Ably::Exceptions::TokenRequestError, 'Key ID and Key Secret are required to generate a new token request' unless request_key_id && request_key_secret
207
+ raise Ably::Exceptions::TokenRequestError, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret
201
208
 
202
209
  timestamp = if token_options[:query_time]
203
210
  client.time
204
211
  else
205
212
  token_options.delete(:timestamp) || Time.now
206
- end.to_i
213
+ end
214
+ timestamp = Time.at(timestamp) if timestamp.kind_of?(Integer)
207
215
 
208
216
  token_request = {
209
- id: request_key_id,
210
- clientId: client_id,
211
- ttl: Ably::Models::Token::DEFAULTS[:ttl],
212
- timestamp: timestamp,
213
- capability: Ably::Models::Token::DEFAULTS[:capability],
214
- nonce: SecureRandom.hex
215
- }.merge(token_options.select { |key, val| token_attributes.include?(key.to_s) })
216
-
217
- if token_request[:capability].is_a?(Hash)
218
- token_request[:capability] = token_request[:capability].to_json
219
- end
217
+ keyName: token_options[:key_name] || request_key_name,
218
+ clientId: token_options[:client_id] || client_id,
219
+ ttl: ((token_options[:ttl] || TOKEN_DEFAULTS.fetch(:ttl)) * 1000).to_i,
220
+ timestamp: (timestamp.to_f * 1000).round,
221
+ capability: token_options[:capability] || TOKEN_DEFAULTS.fetch(:capability),
222
+ nonce: token_options[:nonce] || SecureRandom.hex.force_encoding('UTF-8')
223
+ }
220
224
 
221
- ensure_utf_8 :nonce, token_request[:nonce], allow_nil: true
225
+ token_request[:capability] = JSON.dump(token_request[:capability]) if token_request[:capability].is_a?(Hash)
222
226
 
223
227
  token_request[:mac] = sign_params(token_request, request_key_secret)
224
228
 
225
- convert_to_mixed_case_hash(token_request)
229
+ # Undocumented feature to request a persisted token
230
+ token_request[:persisted] = options[:persisted] if options[:persisted]
231
+
232
+ Models::TokenRequest.new(token_request)
226
233
  end
227
234
 
228
- def api_key
229
- "#{key_id}:#{key_secret}" if api_key_present?
235
+ def key
236
+ "#{key_name}:#{key_secret}" if api_key_present?
230
237
  end
231
238
 
232
- def key_id
233
- options[:key_id]
239
+ def key_name
240
+ options[:key_name]
234
241
  end
235
242
 
236
243
  def key_secret
@@ -245,15 +252,21 @@ module Ably
245
252
  # True when Token Auth is being used to authenticate with Ably
246
253
  def using_token_auth?
247
254
  return options[:use_token_auth] if options.has_key?(:use_token_auth)
248
- token_id || current_token || has_client_id? || token_creatable_externally?
255
+ token || current_token_details || has_client_id? || token_creatable_externally?
249
256
  end
250
257
 
251
258
  def client_id
252
259
  options[:client_id]
253
260
  end
254
261
 
255
- def token_id
256
- options[:token_id]
262
+ def token
263
+ token_object = options[:token] || options[:token_details]
264
+
265
+ if token_object.kind_of?(Ably::Models::TokenDetails)
266
+ token_object.token
267
+ else
268
+ token_object
269
+ end
257
270
  end
258
271
 
259
272
  # Auth header string used in HTTP requests to Ably
@@ -281,13 +294,13 @@ module Ably
281
294
  # True if prerequisites for creating a new token request are present
282
295
  #
283
296
  # One of the following criterion must be met:
284
- # * Valid key id and secret and token_id option not provided as token options cannot be determined
297
+ # * Valid API key and token option not provided as token options cannot be determined
285
298
  # * Authentication callback for new token requests
286
299
  # * Authentication URL for new token requests
287
300
  #
288
301
  # @return [Boolean]
289
302
  def token_renewable?
290
- token_creatable_externally? || (api_key_present? && !token_id)
303
+ token_creatable_externally? || (api_key_present? && !token)
291
304
  end
292
305
 
293
306
  # Returns false when attempting to send an API Key over a non-secure connection
@@ -299,7 +312,45 @@ module Ably
299
312
  end
300
313
 
301
314
  private
302
- attr_reader :client, :default_token_block
315
+ attr_reader :client
316
+
317
+ def ensure_valid_auth_attributes(attributes)
318
+ if attributes[:timestamp]
319
+ unless attributes[:timestamp].kind_of?(Time) || attributes[:timestamp].kind_of?(Numeric)
320
+ raise ArgumentError, ':timestamp must be a Time or positive Integer value of seconds since epoch'
321
+ end
322
+ end
323
+
324
+ if attributes[:ttl]
325
+ unless attributes[:ttl].kind_of?(Numeric) && attributes[:ttl].to_f > 0
326
+ raise ArgumentError, ':ttl must be a positive Numeric value representing time to live in seconds'
327
+ end
328
+ end
329
+
330
+ if attributes[:auth_headers]
331
+ unless attributes[:auth_headers].kind_of?(Hash)
332
+ raise ArgumentError, ':auth_headers must be a valid Hash'
333
+ end
334
+ end
335
+
336
+ if attributes[:auth_params]
337
+ unless attributes[:auth_params].kind_of?(Hash)
338
+ raise ArgumentError, ':auth_params must be a valid Hash'
339
+ end
340
+ end
341
+
342
+ if attributes[:auth_method]
343
+ unless %(get post).include?(attributes[:auth_method].to_s)
344
+ raise ArgumentError, ':auth_method must be either :get or :post'
345
+ end
346
+ end
347
+
348
+ if attributes[:auth_callback]
349
+ unless attributes[:auth_callback].respond_to?(:call)
350
+ raise ArgumentError, ':auth_callback must be a Proc'
351
+ end
352
+ end
353
+ end
303
354
 
304
355
  def ensure_api_key_sent_over_secure_connection
305
356
  raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
@@ -308,44 +359,45 @@ module Ably
308
359
  # Basic Auth HTTP Authorization header value
309
360
  def basic_auth_header
310
361
  ensure_api_key_sent_over_secure_connection
311
- "Basic #{encode64("#{api_key}")}"
362
+ "Basic #{encode64("#{key}")}"
312
363
  end
313
364
 
314
365
  def split_api_key_into_key_and_secret!(options)
315
- api_key_parts = options[:api_key].to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
316
- raise ArgumentError, 'api_key is invalid' unless api_key_parts
366
+ api_key_parts = options[:key].to_s.match(/(?<name>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
367
+ raise ArgumentError, 'key is invalid' unless api_key_parts
317
368
 
318
- options[:key_id] = api_key_parts[:id].encode(Encoding::UTF_8)
369
+ options[:key_name] = api_key_parts[:name].encode(Encoding::UTF_8)
319
370
  options[:key_secret] = api_key_parts[:secret].encode(Encoding::UTF_8)
371
+
372
+ options.delete :key
320
373
  end
321
374
 
322
- def token_auth_id
323
- if token_id
324
- token_id
375
+ # Returns the current token if it exists or authorises and retrieves a token
376
+ def token_auth_string
377
+ if token
378
+ token
325
379
  else
326
- authorise.id
380
+ authorise.token
327
381
  end
328
382
  end
329
383
 
330
384
  # Token Auth HTTP Authorization header value
331
385
  def token_auth_header
332
- "Bearer #{encode64(token_auth_id)}"
386
+ "Bearer #{encode64(token_auth_string)}"
333
387
  end
334
388
 
335
389
  # Basic Auth params to authenticate the Realtime connection
336
390
  def basic_auth_params
337
391
  ensure_api_key_sent_over_secure_connection
338
- # TODO: Change to key_secret when API is updated
339
392
  {
340
- key_id: key_id,
341
- key_value: key_secret
393
+ key: key
342
394
  }
343
395
  end
344
396
 
345
397
  # Token Auth params to authenticate the Realtime connection
346
398
  def token_auth_params
347
399
  {
348
- access_token: token_auth_id
400
+ access_token: token_auth_string
349
401
  }
350
402
  end
351
403
 
@@ -354,13 +406,15 @@ module Ably
354
406
  # @return [Hash]
355
407
  def sign_params(params, secret)
356
408
  text = params.values_at(
357
- :id,
409
+ :keyName,
358
410
  :ttl,
359
411
  :capability,
360
- :client_id,
412
+ :clientId,
361
413
  :timestamp,
362
414
  :nonce
363
- ).map { |t| "#{t}\n" }.join("")
415
+ ).map do |val|
416
+ "#{val}\n"
417
+ end.join('')
364
418
 
365
419
  encode64(
366
420
  OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, text)
@@ -381,7 +435,7 @@ module Ably
381
435
  request.headers = options[:auth_headers] || {}
382
436
  end
383
437
 
384
- unless response.body.kind_of?(Hash)
438
+ if !response.body.kind_of?(Hash) && response.headers['Content-Type'] != 'text/plain'
385
439
  raise Ably::Exceptions::InvalidResponseBody,
386
440
  "Content Type #{response.headers['Content-Type']} is not supported by this client library"
387
441
  end
@@ -423,8 +477,8 @@ module Ably
423
477
  end
424
478
  end
425
479
 
426
- def token_callback_present?
427
- !!default_token_block
480
+ def auth_callback_present?
481
+ !!options[:auth_callback]
428
482
  end
429
483
 
430
484
  def token_url_present?
@@ -432,7 +486,7 @@ module Ably
432
486
  end
433
487
 
434
488
  def token_creatable_externally?
435
- token_callback_present? || token_url_present?
489
+ auth_callback_present? || token_url_present?
436
490
  end
437
491
 
438
492
  def has_client_id?
@@ -440,7 +494,7 @@ module Ably
440
494
  end
441
495
 
442
496
  def api_key_present?
443
- key_id && key_secret
497
+ key_name && key_secret
444
498
  end
445
499
  end
446
500
  end