Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 168
0.00% covered (danger)
0.00%
0 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
TokenTest
0.00% covered (danger)
0.00%
0 / 167
0.00% covered (danger)
0.00%
0 / 17
462
0.00% covered (danger)
0.00%
0 / 1
 setUpBeforeClass
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 tearDownAfterClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 testBaseRequestTokenWithEmptyParams
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 testRequestTokenWithExplicitTimestamp
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 testRequestTokenWithExplicitInvalidTimestamp
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 testRequestWithSystemTimestamp
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 testRequestWithClientId
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 testTokenGenerationWithCapabilityKey
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenGenerationWithSpecifiedKey
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenGenerationWithSpecifiedTTL
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenGenerationWithDefaultTTL
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenGenerationWithExcessiveTTL
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenGenerationWithInvalidTTL
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenRenewalKnownExpiration
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenRenewalUnknownExpiration
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
2
 testTokenRenewalUnknownExpirationFailure
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 testFailingTokenRenewalUnknownExpiration
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2namespace tests;
3use Ably\AblyRest;
4use Ably\Auth;
5use Ably\Exceptions\AblyException;
6
7require_once __DIR__ . '/factories/TestApp.php';
8
9class TokenTest extends \PHPUnit_Framework_TestCase {
10
11    protected static $testApp;
12    protected static $defaultOptions;
13    protected static $ably;
14
15    protected static $errorMarginMs = 10000;
16    protected static $capabilityAll;
17    protected static $tokenParams = [];
18    protected static $defaultTTLms = 3600000; // 1 hour in milliseconds
19
20    public static function setUpBeforeClass() {
21        self::$testApp = new TestApp();
22        self::$defaultOptions = self::$testApp->getOptions();
23        self::$ably = new AblyRest( array_merge( self::$defaultOptions, [
24            'key' => self::$testApp->getAppKeyDefault()->string,
25        ] ) );
26
27        self::$capabilityAll = json_decode(json_encode(['*' => ['*']]));
28    }
29
30    public static function tearDownAfterClass() {
31        self::$testApp->release();
32    }
33
34    /**
35     * Base requestToken case with empty params
36     */
37    public function testBaseRequestTokenWithEmptyParams() {
38        $requestTime = self::$ably->time();
39        $tokenDetails = self::$ably->auth->requestToken();
40        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
41        $this->assertTrue(
42            ($tokenDetails->issued >= $requestTime - self::$errorMarginMs)
43            && ($tokenDetails->issued <= $requestTime + self::$errorMarginMs),
44            'Unexpected issued time'
45        );
46        $this->assertEquals( $tokenDetails->issued + self::$defaultTTLms, $tokenDetails->expires, 'Unexpected expires time' );
47        $this->assertEquals( self::$capabilityAll, json_decode($tokenDetails->capability), 'Unexpected default capability' );
48    }
49
50    /**
51     * requestToken with explicit timestamp
52     */
53    public function testRequestTokenWithExplicitTimestamp() {
54        $requestTime = self::$ably->time();
55        $tokenParams = array_merge(self::$tokenParams, [
56            'timestamp' => $requestTime
57        ]);
58        $tokenDetails = self::$ably->auth->requestToken( $tokenParams );
59        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
60        $this->assertTrue(
61            ($tokenDetails->issued >= $requestTime - self::$errorMarginMs)
62            && ($tokenDetails->issued <= $requestTime + self::$errorMarginMs),
63            'Unexpected issued time'
64        );
65        $this->assertEquals( $tokenDetails->issued + self::$defaultTTLms, $tokenDetails->expires, 'Unexpected expires time' );
66        $this->assertEquals( self::$capabilityAll, json_decode($tokenDetails->capability), 'Unexpected capability' );
67    }
68
69    /**
70     * requestToken with explicit, invalid timestamp
71     */
72    public function testRequestTokenWithExplicitInvalidTimestamp() {
73        $requestTime = self::$ably->time();
74        $tokenParams = array_merge(self::$tokenParams, [
75            'timestamp' => $requestTime - 30 * 60 * 1000 // half an hour ago
76        ]);
77        $this->setExpectedException( 'Ably\Exceptions\AblyException', 'Timestamp not current', 40101 );
78        self::$ably->auth->requestToken( $tokenParams );
79    }
80
81    /**
82     * requestToken with system timestamp
83     */
84    public function testRequestWithSystemTimestamp() {
85        $requestTime = time() * 1000;
86        $authOptions = ['query' => true];
87        $tokenDetails = self::$ably->auth->requestToken( [], $authOptions );
88        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
89        $this->assertTrue(
90            ($tokenDetails->issued >= $requestTime - self::$errorMarginMs)
91            && ($tokenDetails->issued <= $requestTime + self::$errorMarginMs),
92            'Unexpected issued time'
93        );
94        $this->assertEquals( $tokenDetails->issued + self::$defaultTTLms, $tokenDetails->expires, 'Unexpected expires time' );
95        $this->assertEquals( self::$capabilityAll, json_decode($tokenDetails->capability), 'Unexpected capability' );
96    }
97
98    /**
99     * Request token with a clientId specified
100     */
101    public function testRequestWithClientId() {
102        $requestTime = self::$ably->time();
103        $tokenParams = [
104            'clientId' => 'test client id',
105        ];
106        $tokenDetails = self::$ably->auth->requestToken( $tokenParams );
107        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
108        $this->assertTrue(
109            ($tokenDetails->issued >= $requestTime - self::$errorMarginMs)
110            && ($tokenDetails->issued <= $requestTime + self::$errorMarginMs),
111            'Unexpected issued time'
112        );
113        $this->assertEquals( $tokenDetails->issued + self::$defaultTTLms, $tokenDetails->expires, 'Unexpected expires time' );
114        $this->assertEquals( self::$capabilityAll, json_decode($tokenDetails->capability), 'Unexpected capability' );
115        $this->assertEquals( $tokenParams['clientId'], $tokenDetails->clientId, 'Unexpected clientId' );
116    }
117
118    /**
119     * Token generation with capability that subsets key capability
120     */
121    public function testTokenGenerationWithCapabilityKey() {
122        $capability = [ 'onlythischannel' => ['subscribe'] ];
123        $tokenParams = [ 'capability' => $capability ];
124        $tokenDetails = self::$ably->auth->requestToken( $tokenParams );
125        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
126        $this->assertEquals( $capability, (array) json_decode($tokenDetails->capability), 'Unexpected capability' );
127    }
128
129    /**
130     * Token generation with specified key
131     */
132    public function testTokenGenerationWithSpecifiedKey() {
133        $key = self::$testApp->getAppKeyWithCapabilities();
134
135        $authOptions = [
136            'key' => $key->string,
137        ];
138
139        $ably = new AblyRest( array_merge( self::$defaultOptions, [
140            'key' => 'fake.key:veryFake',
141        ] ) );
142
143        $tokenDetails = $ably->auth->requestToken( [], $authOptions ); // fake key should get overridden with the real key
144        $capability_obj = json_decode( $key->capability, false );
145
146        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
147        $this->assertEquals( $capability_obj, json_decode($tokenDetails->capability), 'Unexpected capability' );
148    }
149
150    /**
151     * Token generation with specified ttl
152     */
153    public function testTokenGenerationWithSpecifiedTTL() {
154        $tokenParams = [ 'ttl' => 60 * 1000 ];
155        $tokenDetails = self::$ably->auth->requestToken( $tokenParams );
156        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
157        $this->assertEquals( $tokenDetails->issued + 60 * 1000, $tokenDetails->expires, 'Unexpected expires time' );
158    }
159
160    /**
161     * Token generation with default ttl
162     */
163    public function testTokenGenerationWithDefaultTTL() {
164        $tokenDetails = self::$ably->auth->requestToken();
165        $this->assertNotNull( $tokenDetails->token, 'Expected token id' );
166        $this->assertEquals( $tokenDetails->issued + self::$defaultTTLms, $tokenDetails->expires, 'Expected the default expire time to be 1 hour' );
167    }
168
169    /**
170     * Token generation with excessive ttl
171     */
172    public function testTokenGenerationWithExcessiveTTL() {
173        $oneYearMs = 365*24*3600*1000;
174        $tokenParams = [ 'ttl' => $oneYearMs ];
175        $this->setExpectedException( 'Ably\Exceptions\AblyException', '', 40003 );
176        self::$ably->auth->requestToken( $tokenParams );
177    }
178
179    /**
180     * Token generation with invalid ttl
181     */
182    public function testTokenGenerationWithInvalidTTL() {
183        $tokenParams = [ 'ttl' => -1 * 1000 ];
184        $this->setExpectedException( 'Ably\Exceptions\AblyException', '', 40003 );
185        self::$ably->auth->requestToken( $tokenParams );
186    }
187
188    /**
189     * Automatic token renewal on expiration (known time)
190     */
191    public function testTokenRenewalKnownExpiration() {
192        $ablyKeyAuth = self::$ably;
193
194        $options = array_merge( self::$defaultOptions, [
195            'authCallback' => function( $tokenParams ) use( &$ablyKeyAuth ) {
196                $capability = [ 'testchannel' => ['publish'] ];
197                $tokenParams = [
198                    'ttl' => 2 * 1000 + Auth::TOKEN_EXPIRY_MARGIN, // 2 seconds + expiry margin
199                    'capability' => $capability,
200                ];
201                return $ablyKeyAuth->auth->requestToken( $tokenParams );
202            }
203        ] );
204
205        $ablyTokenAuth = new AblyRest( $options );
206        $ablyTokenAuth->auth->authorize();
207        $tokenBefore = $ablyTokenAuth->auth->getTokenDetails()->token;
208
209        $channel = $ablyTokenAuth->channel( 'testchannel' );
210
211        // do an authorised request
212        $channel->publish( 'test', 'test' );
213        $tokenReq1 = $ablyTokenAuth->auth->getTokenDetails()->token;
214
215        sleep(3);
216
217        $channel->publish( 'test', 'test' );
218        $tokenReq2 = $ablyTokenAuth->auth->getTokenDetails()->token;
219
220        $this->assertEquals( $tokenBefore, $tokenReq1, 'Expected token not to change before expiration' );
221        $this->assertFalse( $tokenReq1 == $tokenReq2, 'Expected token to change after expiration' );
222    }
223
224    /**
225     * Automatic token renewal on expiration (unknown time)
226     */
227    public function testTokenRenewalUnknownExpiration() {
228        $ablyKeyAuth = self::$ably;
229
230        $options = array_merge( self::$defaultOptions, [
231            'authCallback' => function( $tokenParams ) use( &$ablyKeyAuth ) {
232                $capability = [ 'testchannel' => ['publish'] ];
233                $tokenParams = [
234                    'ttl' => 2 * 1000, // 2 seconds
235                    'capability' => $capability,
236                ];
237                $tokenDetails = $ablyKeyAuth->auth->requestToken( $tokenParams );
238                return $tokenDetails->token; // returning just the token string, not TokenDetails => expiry time is unknown
239            }
240        ] );
241
242        $ablyTokenAuth = new AblyRest( $options );
243        $ablyTokenAuth->auth->authorize();
244        $tokenBefore = $ablyTokenAuth->auth->getTokenDetails()->token;
245
246        $channel = $ablyTokenAuth->channel( 'testchannel' );
247
248        // do an authorised request
249        $channel->publish( 'test', 'test' );
250        $tokenReq1 = $ablyTokenAuth->auth->getTokenDetails()->token;
251
252        sleep(3);
253
254        $channel->publish( 'test', 'test' );
255        $tokenReq2 = $ablyTokenAuth->auth->getTokenDetails()->token;
256
257        $this->assertEquals( $tokenBefore, $tokenReq1, 'Expected token not to change before expiration' );
258        $this->assertFalse( $tokenReq1 == $tokenReq2, 'Expected token to change after expiration' );
259    }
260
261    /**
262     * Automatic token renewal failure raises an exception
263     */
264    public function testTokenRenewalUnknownExpirationFailure() {
265        $ablyKeyAuth = self::$ably;
266
267        $tokenParams = [
268            'ttl' => 2 * 1000 // 2 seconds
269        ];
270        $tokenDetails = $ablyKeyAuth->auth->requestToken( $tokenParams );
271        $token = $tokenDetails->token;
272
273        $options = array_merge( self::$defaultOptions, [
274            'authCallback' => function( $tokenParams ) use( &$token ) {
275                return $token;
276            }
277        ] );
278
279        $ablyTokenAuth = new AblyRest( $options );
280        $channel = $ablyTokenAuth->channel( 'testchannel' );
281
282        // do an authorised request with the valid token
283        $channel->publish( 'test', 'test' );
284
285        sleep(3);
286
287        $this->setExpectedException( 'Ably\Exceptions\AblyException', '', 40142 ); // token expired
288        $channel->publish( 'test', 'test' ); // token is no longer valid
289    }
290
291    /**
292     * Automatic token renewal on expiration (unknown time) should fail with no means of renewal
293     */
294    public function testFailingTokenRenewalUnknownExpiration() {
295        $ablyKeyAuth = self::$ably;
296
297        $tokenParams = [
298            'ttl' => 2 * 1000, // 2 seconds
299        ];
300        $tokenDetails = $ablyKeyAuth->auth->requestToken( $tokenParams );
301
302        $options = array_merge( self::$defaultOptions, [
303            'token' => $tokenDetails->token,
304        ] );
305        $ablyTokenAuth = new AblyRest( $options );
306        $channel = $ablyTokenAuth->channel( 'testchannel' );
307        $channel->publish( 'test', 'test' ); // this should work
308
309        sleep(3);
310
311        $this->setExpectedException( 'Ably\Exceptions\AblyException', '', 40101 );
312        $channel->publish( 'test', 'test' ); // this should fail
313    }
314}