ably-rest 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/SPEC.md +1380 -631
  4. data/ably-rest.gemspec +11 -5
  5. data/lib/submodules/ably-ruby/.travis.yml +1 -1
  6. data/lib/submodules/ably-ruby/CHANGELOG.md +42 -48
  7. data/lib/submodules/ably-ruby/ably.gemspec +7 -1
  8. data/lib/submodules/ably-ruby/lib/ably.rb +2 -0
  9. data/lib/submodules/ably-ruby/lib/ably/auth.rb +155 -47
  10. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +2 -0
  11. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +2 -3
  12. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +54 -0
  13. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +14 -4
  14. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +13 -7
  15. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +1 -2
  16. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +3 -2
  17. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +1 -3
  18. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +2 -2
  19. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +6 -0
  20. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +15 -4
  21. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -0
  22. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +10 -3
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +62 -6
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +58 -54
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +18 -5
  27. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +9 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +32 -14
  29. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  30. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  31. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +251 -11
  32. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +12 -2
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +316 -24
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +93 -1
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +177 -86
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +284 -60
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +45 -6
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +4 -0
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +181 -49
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +13 -0
  41. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +222 -4
  42. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +132 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -28
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +7 -7
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +10 -0
  46. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +41 -17
  47. data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -0
  48. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +16 -0
  49. data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +60 -0
  50. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +45 -0
  51. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -1
  52. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +6 -5
  53. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +5 -1
  54. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +5 -1
  55. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +5 -1
  56. metadata +57 -13
@@ -14,10 +14,10 @@ Gem::Specification.new do |spec|
14
14
  spec.version = Ably::VERSION
15
15
  spec.authors = ['Matthew O\'Riordan']
16
16
  spec.email = ['matt@ably.io']
17
- spec.description = %q{A Ruby REST only client library for ably.io, the real-time messaging service}
18
- spec.summary = %q{A Ruby REST only client library for ably.io, the real-time messaging service}
17
+ spec.description = %q{A Ruby REST only client library for ably.io realtime messaging}
18
+ spec.summary = %q{A Ruby REST only client library for ably.io realtime messaging}
19
19
  spec.homepage = 'http://github.com/ably/ably-ruby-rest'
20
- spec.license = 'MIT'
20
+ spec.license = 'Apache 2'
21
21
 
22
22
  submodule_path = File.expand_path('../lib/submodules/ably-ruby', __FILE__)
23
23
  submodule_files = Dir.chdir(submodule_path) do
@@ -33,13 +33,19 @@ Gem::Specification.new do |spec|
33
33
 
34
34
  spec.add_runtime_dependency 'faraday', '~> 0.9'
35
35
  spec.add_runtime_dependency 'json'
36
- spec.add_runtime_dependency 'msgpack-ably', '~> 0.5.10'
36
+ spec.add_runtime_dependency 'msgpack', '>= 0.6.2'
37
+ spec.add_runtime_dependency 'addressable', '>= 2.0.0'
37
38
 
38
39
  spec.add_development_dependency 'bundler', '~> 1.3'
39
40
  spec.add_development_dependency 'rake'
40
41
  spec.add_development_dependency 'redcarpet'
41
- spec.add_development_dependency 'rspec', '~> 3.0'
42
+ spec.add_development_dependency 'rspec', '~> 3.2.0'
42
43
  spec.add_development_dependency 'rspec-retry'
43
44
  spec.add_development_dependency 'yard'
44
45
  spec.add_development_dependency 'webmock'
46
+
47
+ if RUBY_VERSION.match(/^2/)
48
+ spec.add_development_dependency 'pry'
49
+ spec.add_development_dependency 'pry-byebug'
50
+ end
45
51
  end
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
- env: RSPEC_RETRY=true
2
+ env: RSPEC_RETRY=false
3
3
  language: ruby
4
4
  rvm:
5
5
  - 1.9.3
@@ -1,29 +1,59 @@
1
1
  # Change Log
2
2
 
3
- ## [Unreleased](https://github.com/ably/ably-ruby/tree/HEAD)
3
+ ## [v0.8.6](https://github.com/ably/ably-ruby/tree/v0.8.6) (2015-12-02)
4
4
 
5
- [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.2...HEAD)
5
+ [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.5...v0.8.6)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Some intermittent test fixes & enable tests that were blocked [\#70](https://github.com/ably/ably-ruby/pull/70) ([mattheworiordan](https://github.com/mattheworiordan))
10
+
11
+ - Output detailed log for any text failures [\#67](https://github.com/ably/ably-ruby/pull/67) ([mattheworiordan](https://github.com/mattheworiordan))
12
+
13
+ - 0.8 final spec \(98% compliance\) [\#66](https://github.com/ably/ably-ruby/pull/66) ([mattheworiordan](https://github.com/mattheworiordan))
14
+
15
+ ## [v0.8.5](https://github.com/ably/ably-ruby/tree/v0.8.5) (2015-10-08)
16
+ [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.4...v0.8.5)
6
17
 
7
18
  **Implemented enhancements:**
8
19
 
9
- - Add compatibility support for default Crypto params [\#53](https://github.com/ably/ably-ruby/issues/53)
20
+ - Switch arity of auth methods [\#61](https://github.com/ably/ably-ruby/issues/61)
10
21
 
11
- - EventEmitter on connection [\#52](https://github.com/ably/ably-ruby/issues/52)
22
+ **Fixed bugs:**
12
23
 
24
+ - Switch arity of auth methods [\#61](https://github.com/ably/ably-ruby/issues/61)
25
+ - Add test: Message published, connection dropped, then restores to point before last message was published [\#56](https://github.com/ably/ably-ruby/issues/56)
26
+ - Documentation for constructor is incorrect [\#49](https://github.com/ably/ably-ruby/issues/49)
27
+
28
+ **Merged pull requests:**
29
+
30
+ - Ensure connections are always closed in tests [\#63](https://github.com/ably/ably-ruby/pull/63) ([mattheworiordan](https://github.com/mattheworiordan))
31
+
32
+ ## [v0.8.4](https://github.com/ably/ably-ruby/tree/v0.8.4) (2015-09-08)
33
+ [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.3...v0.8.4)
34
+
35
+ **Implemented enhancements:**
36
+
37
+ - Add compatibility support for default Crypto params [\#53](https://github.com/ably/ably-ruby/issues/53)
38
+ - EventEmitter on connection [\#52](https://github.com/ably/ably-ruby/issues/52)
13
39
  - Add test for connectionId attribute for a message sent over REST [\#50](https://github.com/ably/ably-ruby/issues/50)
14
40
 
15
- - Implement :queue\_messages option [\#36](https://github.com/ably/ably-ruby/issues/36)
41
+ **Merged pull requests:**
16
42
 
17
- - Check that a non 200-299 status code for REST requests uses fallback hosts [\#35](https://github.com/ably/ably-ruby/issues/35)
43
+ - Spec update to fix a number of issues [\#60](https://github.com/ably/ably-ruby/pull/60) ([mattheworiordan](https://github.com/mattheworiordan))
44
+ - Allow clientId to be provided on init if using externally created token [\#58](https://github.com/ably/ably-ruby/pull/58) ([SimonWoolf](https://github.com/SimonWoolf))
18
45
 
19
- - Move stats fixtures into ably-common [\#34](https://github.com/ably/ably-ruby/issues/34)
46
+ ## [v0.8.3](https://github.com/ably/ably-ruby/tree/v0.8.3) (2015-08-19)
47
+ [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.2...v0.8.3)
20
48
 
21
- - Add tests for messages with no data or name fields [\#21](https://github.com/ably/ably-ruby/issues/21)
49
+ **Implemented enhancements:**
22
50
 
51
+ - Implement :queue\_messages option [\#36](https://github.com/ably/ably-ruby/issues/36)
52
+ - Check that a non 200-299 status code for REST requests uses fallback hosts [\#35](https://github.com/ably/ably-ruby/issues/35)
53
+ - Move stats fixtures into ably-common [\#34](https://github.com/ably/ably-ruby/issues/34)
54
+ - Add tests for messages with no data or name fields [\#21](https://github.com/ably/ably-ruby/issues/21)
23
55
  - Namespace MsgPack as MsgPack5 because compliance is not merged in [\#12](https://github.com/ably/ably-ruby/issues/12)
24
-
25
56
  - Add test coverage for receiving messages more than once i.e. historical messages resent somehow on reconnect [\#11](https://github.com/ably/ably-ruby/issues/11)
26
-
27
57
  - Add async methods for Authentication in the realtime library [\#8](https://github.com/ably/ably-ruby/issues/8)
28
58
 
29
59
  **Fixed bugs:**
@@ -33,41 +63,28 @@
33
63
  **Closed issues:**
34
64
 
35
65
  - Scope default token params in arguments [\#55](https://github.com/ably/ably-ruby/issues/55)
36
-
37
66
  - Channel options can be reset when accessing a channel with \#get [\#46](https://github.com/ably/ably-ruby/issues/46)
38
67
 
39
68
  **Merged pull requests:**
40
69
 
41
- - Spec update to fix a number of issues [\#60](https://github.com/ably/ably-ruby/pull/60) ([mattheworiordan](https://github.com/mattheworiordan))
42
-
43
- - Allow clientId to be provided on init if using externally created token [\#58](https://github.com/ably/ably-ruby/pull/58) ([SimonWoolf](https://github.com/SimonWoolf))
44
-
45
70
  - Separate token params for auth [\#57](https://github.com/ably/ably-ruby/pull/57) ([mattheworiordan](https://github.com/mattheworiordan))
46
-
47
71
  - Ensure files are required in a consistent order [\#51](https://github.com/ably/ably-ruby/pull/51) ([SimonWoolf](https://github.com/SimonWoolf))
48
72
 
49
73
  ## [v0.8.2](https://github.com/ably/ably-ruby/tree/v0.8.2) (2015-05-20)
50
-
51
74
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.1...v0.8.2)
52
75
 
53
76
  **Implemented enhancements:**
54
77
 
55
78
  - Ensure Array object can be used in place of Hash for payload [\#44](https://github.com/ably/ably-ruby/issues/44)
56
-
57
79
  - Change connect\_automatically option to auto\_connect for consistency [\#42](https://github.com/ably/ably-ruby/issues/42)
58
-
59
80
  - Rename PaginatedResource to PaginatedResult for consistency [\#40](https://github.com/ably/ably-ruby/issues/40)
60
-
61
81
  - EventEmitter should use `emit` not `trigger` to be consistent with other libs [\#31](https://github.com/ably/ably-ruby/issues/31)
62
-
63
82
  - Add exceptions when data attribute for messages/presence is not String, Binary or JSON data [\#4](https://github.com/ably/ably-ruby/issues/4)
64
-
65
83
  - Auth Callback and Auth URL should support tokens as well as token requests [\#2](https://github.com/ably/ably-ruby/issues/2)
66
84
 
67
85
  **Closed issues:**
68
86
 
69
87
  - Realtime Presence\#get does not wait by default [\#47](https://github.com/ably/ably-ruby/issues/47)
70
-
71
88
  - No implicit attach when accessing channel.presence [\#45](https://github.com/ably/ably-ruby/issues/45)
72
89
 
73
90
  **Merged pull requests:**
@@ -75,11 +92,9 @@
75
92
  - Reject invalid payload type [\#48](https://github.com/ably/ably-ruby/pull/48) ([mattheworiordan](https://github.com/mattheworiordan))
76
93
 
77
94
  ## [v0.8.1](https://github.com/ably/ably-ruby/tree/v0.8.1) (2015-04-23)
78
-
79
95
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.8.0...v0.8.1)
80
96
 
81
97
  ## [v0.8.0](https://github.com/ably/ably-ruby/tree/v0.8.0) (2015-04-23)
82
-
83
98
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.6...v0.8.0)
84
99
 
85
100
  **Merged pull requests:**
@@ -87,18 +102,15 @@
87
102
  - Token naming refactor [\#29](https://github.com/ably/ably-ruby/pull/29) ([mattheworiordan](https://github.com/mattheworiordan))
88
103
 
89
104
  ## [v0.7.6](https://github.com/ably/ably-ruby/tree/v0.7.6) (2015-04-17)
90
-
91
105
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.5...v0.7.6)
92
106
 
93
107
  **Implemented enhancements:**
94
108
 
95
109
  - Rename Stat to Stats for consistency [\#32](https://github.com/ably/ably-ruby/issues/32)
96
-
97
110
  - Stats objects [\#24](https://github.com/ably/ably-ruby/issues/24)
98
-
99
111
  - Need a test to handle errors in callbacks [\#13](https://github.com/ably/ably-ruby/issues/13)
100
-
101
112
  - Allow token ID or API key in the client constructor [\#5](https://github.com/ably/ably-ruby/issues/5)
113
+ - Typed stats similar to Java library + zero default for empty stats [\#25](https://github.com/ably/ably-ruby/pull/25) ([mattheworiordan](https://github.com/mattheworiordan))
102
114
 
103
115
  **Fixed bugs:**
104
116
 
@@ -111,19 +123,14 @@
111
123
  **Merged pull requests:**
112
124
 
113
125
  - Test encoded presence fixture data for \#get & \#history [\#28](https://github.com/ably/ably-ruby/pull/28) ([mattheworiordan](https://github.com/mattheworiordan))
114
-
115
126
  - Add coveralls.io coverage reporting [\#27](https://github.com/ably/ably-ruby/pull/27) ([mattheworiordan](https://github.com/mattheworiordan))
116
-
117
127
  - New paginated resource [\#26](https://github.com/ably/ably-ruby/pull/26) ([mattheworiordan](https://github.com/mattheworiordan))
118
-
119
- - Typed stats similar to Java library + zero default for empty stats [\#25](https://github.com/ably/ably-ruby/pull/25) ([mattheworiordan](https://github.com/mattheworiordan))
128
+ - History since attach [\#22](https://github.com/ably/ably-ruby/pull/22) ([mattheworiordan](https://github.com/mattheworiordan))
120
129
 
121
130
  ## [v0.7.5](https://github.com/ably/ably-ruby/tree/v0.7.5) (2015-03-21)
122
-
123
131
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.4...v0.7.5)
124
132
 
125
133
  ## [v0.7.4](https://github.com/ably/ably-ruby/tree/v0.7.4) (2015-03-21)
126
-
127
134
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.2...v0.7.4)
128
135
 
129
136
  **Merged pull requests:**
@@ -131,7 +138,6 @@
131
138
  - Presence Member Map [\#14](https://github.com/ably/ably-ruby/pull/14) ([mattheworiordan](https://github.com/mattheworiordan))
132
139
 
133
140
  ## [v0.7.2](https://github.com/ably/ably-ruby/tree/v0.7.2) (2015-02-10)
134
-
135
141
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.1...v0.7.2)
136
142
 
137
143
  **Implemented enhancements:**
@@ -141,15 +147,12 @@
141
147
  **Merged pull requests:**
142
148
 
143
149
  - Update README to include various missing snippets for core features [\#9](https://github.com/ably/ably-ruby/pull/9) ([kouno](https://github.com/kouno))
144
-
145
150
  - Fix connection retry frequency [\#7](https://github.com/ably/ably-ruby/pull/7) ([kouno](https://github.com/kouno))
146
151
 
147
152
  ## [v0.7.1](https://github.com/ably/ably-ruby/tree/v0.7.1) (2015-01-18)
148
-
149
153
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.7.0...v0.7.1)
150
154
 
151
155
  ## [v0.7.0](https://github.com/ably/ably-ruby/tree/v0.7.0) (2015-01-12)
152
-
153
156
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.6.2...v0.7.0)
154
157
 
155
158
  **Closed issues:**
@@ -157,39 +160,30 @@
157
160
  - JSON encoder should only append utf-8 before a cipher encoder is applied [\#1](https://github.com/ably/ably-ruby/issues/1)
158
161
 
159
162
  ## [v0.6.2](https://github.com/ably/ably-ruby/tree/v0.6.2) (2014-12-10)
160
-
161
163
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.2.0...v0.6.2)
162
164
 
163
165
  ## [v0.2.0](https://github.com/ably/ably-ruby/tree/v0.2.0) (2014-12-09)
164
-
165
166
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.6...v0.2.0)
166
167
 
167
168
  ## [v0.1.6](https://github.com/ably/ably-ruby/tree/v0.1.6) (2014-10-31)
168
-
169
169
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.5...v0.1.6)
170
170
 
171
171
  ## [v0.1.5](https://github.com/ably/ably-ruby/tree/v0.1.5) (2014-10-23)
172
-
173
172
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.4...v0.1.5)
174
173
 
175
174
  ## [v0.1.4](https://github.com/ably/ably-ruby/tree/v0.1.4) (2014-09-27)
176
-
177
175
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.3...v0.1.4)
178
176
 
179
177
  ## [v0.1.3](https://github.com/ably/ably-ruby/tree/v0.1.3) (2014-09-26)
180
-
181
178
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.2...v0.1.3)
182
179
 
183
180
  ## [v0.1.2](https://github.com/ably/ably-ruby/tree/v0.1.2) (2014-09-25)
184
-
185
181
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.1...v0.1.2)
186
182
 
187
183
  ## [v0.1.1](https://github.com/ably/ably-ruby/tree/v0.1.1) (2014-09-23)
188
-
189
184
  [Full Changelog](https://github.com/ably/ably-ruby/compare/v0.1.0...v0.1.1)
190
185
 
191
186
  ## [v0.1.0](https://github.com/ably/ably-ruby/tree/v0.1.0) (2014-09-23)
192
187
 
193
188
 
194
-
195
189
  \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
@@ -25,14 +25,20 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency 'json'
26
26
  spec.add_runtime_dependency 'websocket-driver', '~> 0.3'
27
27
  spec.add_runtime_dependency 'msgpack', '>= 0.6.2'
28
+ spec.add_runtime_dependency 'addressable', '>= 2.0.0'
28
29
 
29
30
  spec.add_development_dependency 'bundler', '~> 1.3'
30
31
  spec.add_development_dependency 'rake'
31
32
  spec.add_development_dependency 'redcarpet'
32
- spec.add_development_dependency 'rspec', '~> 3.1.0' # version lock, see config.around(:example, :event_machine) in event_machine_helper.rb
33
+ spec.add_development_dependency 'rspec', '~> 3.2.0' # version lock, see config.around(:example, :event_machine) in event_machine_helper.rb
33
34
  spec.add_development_dependency 'rspec-retry'
34
35
  spec.add_development_dependency 'yard'
35
36
  spec.add_development_dependency 'webmock'
36
37
 
37
38
  spec.add_development_dependency 'coveralls'
39
+
40
+ if RUBY_VERSION.match(/^2/)
41
+ spec.add_development_dependency 'pry'
42
+ spec.add_development_dependency 'pry-byebug'
43
+ end
38
44
  end
@@ -1,3 +1,5 @@
1
+ require 'addressable/uri'
2
+
1
3
  %w(modules util).each do |namespace|
2
4
  Dir.glob(File.expand_path("ably/#{namespace}/*.rb", File.dirname(__FILE__))).sort.each do |file|
3
5
  require file
@@ -13,8 +13,6 @@ module Ably
13
13
  # @return [String] The provided client ID, used for identifying this client for presence purposes
14
14
  # @!attribute [r] current_token_details
15
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
16
  # @!attribute [r] key
19
17
  # @return [String] Complete API key containing both the key name and key secret, if present
20
18
  # @!attribute [r] key_name
@@ -61,6 +59,7 @@ module Ably
61
59
  @client = client
62
60
  @options = auth_options.dup
63
61
  @token_params = token_params.dup
62
+ @token_option = options[:token] || options[:token_details]
64
63
 
65
64
  @options.delete :force # Forcing token auth for every request is not a valid default
66
65
 
@@ -74,11 +73,25 @@ module Ably
74
73
  raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
75
74
  end
76
75
 
77
- if has_client_id? && !token_creatable_externally?
78
- 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?
76
+ if options[:client_id] == '*'
77
+ raise ArgumentError, 'A client cannot be configured with a wildcard client_id'
78
+ end
79
+
80
+ if has_client_id? && !token_creatable_externally? && !token_option
81
+ raise ArgumentError, 'client_id cannot be provided without a complete API key or means to authenticate. An API key is needed to automatically authenticate with Ably and obtain a token' unless api_key_present?
79
82
  ensure_utf_8 :client_id, client_id
80
83
  end
81
84
 
85
+ # If a token details object or token string is provided in the initializer
86
+ # then the client can be authorised immediately using this token
87
+ if token_option
88
+ token_details = convert_to_token_details(token_option)
89
+ if token_details
90
+ token_details = authorise_with_token(token_details)
91
+ logger.debug "Auth: new token passed in to the initializer: #{token_details}"
92
+ end
93
+ end
94
+
82
95
  @options.freeze
83
96
  @token_params.freeze
84
97
  end
@@ -120,7 +133,9 @@ module Ably
120
133
  token_params = (auth_options.delete(:token_params) || {}).merge(token_params)
121
134
  @token_params = @token_params.merge(token_params) # update defaults
122
135
 
123
- @current_token_details = request_token(token_params, auth_options)
136
+ authorise_with_token(request_token(token_params, auth_options)).tap do |new_token_details|
137
+ logger.debug "Auth: new token following authorisation: #{new_token_details}"
138
+ end
124
139
  end
125
140
 
126
141
  # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
@@ -154,34 +169,29 @@ module Ably
154
169
  def request_token(token_params = {}, auth_options = {})
155
170
  ensure_valid_auth_attributes auth_options
156
171
 
157
- token_params = (auth_options[:token_params] || {}).merge(token_params)
158
- token_params = self.token_params.merge(token_params)
172
+ # Token param precedence (lowest to highest):
173
+ # Auth default => client_id => auth_options[:token_params] arg => token_params arg
174
+ token_params = self.token_params.merge(
175
+ (client_id ? { client_id: client_id } : {}).
176
+ merge(auth_options[:token_params] || {}).
177
+ merge(token_params)
178
+ )
179
+
159
180
  auth_options = self.options.merge(auth_options)
160
181
 
161
182
  token_request = if auth_callback = auth_options.delete(:auth_callback)
162
183
  auth_callback.call(token_params)
163
184
  elsif auth_url = auth_options.delete(:auth_url)
164
- token_request_from_auth_url(auth_url, auth_options)
185
+ token_request_from_auth_url(auth_url, auth_options, token_params)
165
186
  else
166
187
  create_token_request(token_params, auth_options)
167
188
  end
168
189
 
169
- case token_request
170
- when Ably::Models::TokenDetails
171
- return token_request
172
- when Hash
173
- return Ably::Models::TokenDetails.new(token_request) if IdiomaticRubyWrapper(token_request).has_key?(:issued)
174
- when String
175
- return Ably::Models::TokenDetails.new(token: token_request)
190
+ convert_to_token_details(token_request).tap do |token_details|
191
+ return token_details if token_details
176
192
  end
177
193
 
178
- token_request = Ably::Models::TokenRequest(token_request)
179
-
180
- response = client.post("/keys/#{token_request.key_name}/requestToken",
181
- token_request.hash, send_auth_header: false,
182
- disable_automatic_reauthorise: true)
183
-
184
- Ably::Models::TokenDetails.new(response.body)
194
+ send_token_request(token_request)
185
195
  end
186
196
 
187
197
  # Creates and signs a token request that can then subsequently be used by any client to request a token
@@ -277,21 +287,23 @@ module Ably
277
287
  # True when Token Auth is being used to authenticate with Ably
278
288
  def using_token_auth?
279
289
  return options[:use_token_auth] if options.has_key?(:use_token_auth)
280
- !!(token || current_token_details || has_client_id? || token_creatable_externally?)
290
+ !!(token_option || current_token_details || has_client_id? || token_creatable_externally?)
281
291
  end
282
292
 
283
293
  def client_id
284
- options[:client_id]
294
+ @client_id || options[:client_id]
285
295
  end
286
296
 
287
- def token
288
- token_object = options[:token] || options[:token_details]
289
-
290
- if token_object.kind_of?(Ably::Models::TokenDetails)
291
- token_object.token
292
- else
293
- token_object
294
- end
297
+ # When a client has authenticated with Ably and the client is either anonymous (cannot assume a +client_id+)
298
+ # or has an assigned +client_id+ (implicit in all operations), then this client has a validated +client_id+, even
299
+ # if that client_id is +nil+ (anonymous)
300
+ #
301
+ # Once validated by Ably, the client library will enforce the use of the +client_id+ identity provided by Ably, rejecting
302
+ # messages with an invalid +client_id+ immediately
303
+ #
304
+ # @return [Boolean]
305
+ def client_id_validated?
306
+ !!@client_id_validated
295
307
  end
296
308
 
297
309
  # Auth header string used in HTTP requests to Ably
@@ -327,7 +339,7 @@ module Ably
327
339
  #
328
340
  # @return [Boolean]
329
341
  def token_renewable?
330
- token_creatable_externally? || (api_key_present? && !token)
342
+ token_creatable_externally? || (api_key_present? && !token_option)
331
343
  end
332
344
 
333
345
  # Returns false when attempting to send an API Key over a non-secure connection
@@ -338,8 +350,59 @@ module Ably
338
350
  client.use_tls? || using_token_auth?
339
351
  end
340
352
 
353
+ # True if token provided client_id is compatible with the client's configured +client_id+, when applicable
354
+ #
355
+ # @return [Boolean]
356
+ # @api private
357
+ def token_client_id_allowed?(token_client_id)
358
+ return true if client_id.nil? # no explicit client_id specified for this client
359
+ return true if client_id == '*' || token_client_id == '*' # wildcard supported always
360
+ token_client_id == client_id
361
+ end
362
+
363
+ # True if assumed_client_id is compatible with the client's configured or Ably assigned +client_id+
364
+ #
365
+ # @return [Boolean]
366
+ # @api private
367
+ def can_assume_client_id?(assumed_client_id)
368
+ if client_id_validated?
369
+ client_id == '*' || (client_id == assumed_client_id)
370
+ elsif !options[:client_id] || options[:client_id] == '*'
371
+ true # client ID is unknown
372
+ else
373
+ options[:client_id] == assumed_client_id
374
+ end
375
+ end
376
+
377
+ # Configures the client ID for this client
378
+ # Typically this occurs following an Auth or receiving a {Ably::Models::ProtocolMessage} with a +client_id+ in the {Ably::Models::ConnectionDetails}
379
+ #
380
+ # @api private
381
+ def configure_client_id(new_client_id)
382
+ # If new client ID from Ably is a wildcard, but preconfigured clientId is set, then keep the existing clientId
383
+ if has_client_id? && new_client_id == '*'
384
+ @client_id_validated = true
385
+ return
386
+ end
387
+
388
+ # If client_id is defined and not a wildcard, prevent it changing, this is not supported
389
+ if client_id && client_id != '*' && new_client_id != client_id
390
+ raise Ably::Exceptions::IncompatibleClientId.new("Client ID is immutable once configured for a client. Client ID cannot be changed to '#{new_client_id}'", 400, 40012)
391
+ end
392
+ @client_id_validated = true
393
+ @client_id = new_client_id
394
+ end
395
+
396
+ # True when a client_id other than a wildcard is configured for Auth
397
+ #
398
+ # @api private
399
+ def has_client_id?
400
+ client_id && (client_id != '*')
401
+ end
402
+
341
403
  private
342
404
  attr_reader :client
405
+ attr_reader :token_option
343
406
 
344
407
  def ensure_valid_auth_attributes(attributes)
345
408
  if attributes[:timestamp]
@@ -401,17 +464,21 @@ module Ably
401
464
 
402
465
  # Returns the current token if it exists or authorises and retrieves a token
403
466
  def token_auth_string
404
- # If a TokenDetails object has been issued by this library
405
- # then that Token will take precedence
406
- if @current_token_details
407
- authorise.token
408
- elsif token # token string was configured in the options
409
- token
467
+ if !current_token_details && token_option
468
+ # A TokenRequest was configured in the ClientOptions +:token field+ and no current token exists
469
+ # Note: If a Token or TokenDetails is provided in the initializer, the token is stored in +current_token_details+
470
+ authorise_with_token send_token_request(token_option)
471
+ current_token_details.token
410
472
  else
473
+ # Authorise will use the current token if one exists and is not expired, otherwise a new token will be issued
411
474
  authorise.token
412
475
  end
413
476
  end
414
477
 
478
+ def configure_current_token_details(token_details)
479
+ @current_token_details = token_details
480
+ end
481
+
415
482
  # Token Auth HTTP Authorization header value
416
483
  def token_auth_header
417
484
  "Bearer #{encode64(token_auth_string)}"
@@ -455,15 +522,20 @@ module Ably
455
522
  # Retrieve a token request from a specified URL, expects a JSON response
456
523
  #
457
524
  # @return [Hash]
458
- def token_request_from_auth_url(auth_url, auth_options)
525
+ def token_request_from_auth_url(auth_url, auth_options, token_params)
459
526
  uri = URI.parse(auth_url)
460
527
  connection = Faraday.new("#{uri.scheme}://#{uri.host}", connection_options)
461
- method = auth_options[:auth_method] || :get
528
+ method = auth_options[:auth_method] || options[:auth_method] || :get
529
+ params = (auth_options[:auth_params] || options[:auth_method] || {}).merge(token_params)
462
530
 
463
531
  response = connection.send(method) do |request|
464
532
  request.url uri.path
465
- request.params = CGI.parse(uri.query || '').merge(auth_options[:auth_params] || {})
466
533
  request.headers = auth_options[:auth_headers] || {}
534
+ if method.to_s.downcase == 'post'
535
+ request.body = params
536
+ else
537
+ request.params = (Addressable::URI.parse(uri.to_s).query_values || {}).merge(params)
538
+ end
467
539
  end
468
540
 
469
541
  if !response.body.kind_of?(Hash) && !response.headers['Content-Type'].to_s.match(%r{text/plain}i)
@@ -474,6 +546,42 @@ module Ably
474
546
  response.body
475
547
  end
476
548
 
549
+ # Use the provided token to authenticate immediately and store the token details in +current_token_details+
550
+ def authorise_with_token(new_token_details)
551
+ if new_token_details && !new_token_details.from_token_string?
552
+ if !token_client_id_allowed?(new_token_details.client_id)
553
+ raise Ably::Exceptions::IncompatibleClientId.new("Client ID '#{new_token_details.client_id}' in the token is incompatible with the current client ID '#{client_id}'", 400, 40012)
554
+ end
555
+ configure_client_id new_token_details.client_id
556
+ end
557
+ configure_current_token_details new_token_details
558
+ end
559
+
560
+ # Returns a TokenDetails object if the provided token_details_obj argument is a TokenDetails object, Token String
561
+ # or TokenDetails JSON object.
562
+ # If the token_details_obj is not a Token or TokenDetails +nil+ is returned
563
+ def convert_to_token_details(token_details_obj)
564
+ case token_details_obj
565
+ when Ably::Models::TokenDetails
566
+ return token_details_obj
567
+ when Hash
568
+ return Ably::Models::TokenDetails.new(token_details_obj) if IdiomaticRubyWrapper(token_details_obj).has_key?(:issued)
569
+ when String
570
+ return Ably::Models::TokenDetails.new(token: token_details_obj)
571
+ end
572
+ end
573
+
574
+ # @return [Ably::Models::TokenDetails]
575
+ def send_token_request(token_request)
576
+ token_request = Ably::Models::TokenRequest(token_request)
577
+
578
+ response = client.post("/keys/#{token_request.key_name}/requestToken",
579
+ token_request.hash, send_auth_header: false,
580
+ disable_automatic_reauthorise: true)
581
+
582
+ Ably::Models::TokenDetails.new(response.body)
583
+ end
584
+
477
585
  # Return a Hash of connection options to initiate the Faraday::Connection with
478
586
  #
479
587
  # @return [Hash]
@@ -501,7 +609,7 @@ module Ably
501
609
  # Raise exceptions if response code is invalid
502
610
  builder.use Ably::Rest::Middleware::ExternalExceptions
503
611
 
504
- setup_incoming_middleware builder, client.logger
612
+ setup_incoming_middleware builder, logger
505
613
 
506
614
  # Set Faraday's HTTP adapter
507
615
  builder.adapter Faraday.default_adapter
@@ -520,12 +628,12 @@ module Ably
520
628
  auth_callback_present? || token_url_present?
521
629
  end
522
630
 
523
- def has_client_id?
524
- !!client_id
525
- end
526
-
527
631
  def api_key_present?
528
632
  key_name && key_secret
529
633
  end
634
+
635
+ def logger
636
+ client.logger
637
+ end
530
638
  end
531
639
  end