PHPSDKTestCase   F
last analyzed

Complexity

Total Complexity 135

Size/Duplication

Total Lines 1923
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 15

Test Coverage

Coverage 0%
Metric Value
dl 0
loc 1923
ccs 0
cts 1679
cp 0
rs 0.6314
wmc 135
lcom 2
cbo 15

122 Methods

Rating   Name   Duplication   Size   Complexity  
A kValidSignedRequest() 0 12 1
A kNonTosedSignedRequest() 0 7 1
A kSignedRequestWithEmptyValue() 0 3 1
A kSignedRequestWithBogusSignature() 0 11 1
A kSignedRequestWithWrongAlgo() 0 12 1
A testConstructor() 0 10 1
A testConstructorWithFileUpload() 0 16 1
A testSetAppId() 0 9 1
A testSetAPISecret() 0 9 1
A testSetAPPSecret() 0 9 1
A testSetAccessToken() 0 10 1
A testSetFileUploadSupport() 0 17 1
B testGetCurrentURL() 0 35 1
B testGetLoginURL() 0 24 1
B testGetLoginURLWithExtraParams() 0 27 1
B testGetLoginURLWithScopeParamsAsArray() 0 32 1
A testGetCodeWithValidCSRFState() 0 13 1
A testGetCodeWithInvalidCSRFState() 0 12 1
A testGetCodeWithMissingCSRFState() 0 11 1
A testPersistentCSRFState() 0 17 1
A testPersistentCSRFStateWithSharedSession() 0 20 1
A testGetUserFromSignedRequest() 0 10 1
A testDisallowSignedRequest() 0 11 1
B testSignedRequestRewrite() 0 27 1
A testGetSignedRequestFromCookie() 0 12 1
A testGetSignedRequestWithIncorrectSignature() 0 10 1
A testNonUserAccessToken() 0 12 1
A testMissingMetadataCookie() 0 7 1
A testEmptyMetadataCookie() 0 8 1
A testMetadataCookie() 0 10 1
A testQuotedMetadataCookie() 0 10 1
A testAPIForLoggedOutUsers() 0 14 1
B testAPIWithBogusAccessToken() 0 26 2
A testAPIGraphPublicData() 0 10 1
A testGraphAPIWithBogusAccessToken() 0 17 2
A testGraphAPIWithExpiredAccessToken() 0 17 2
A testGraphAPIOAuthSpecError() 0 19 2
A testGraphAPIMethodOAuthSpecError() 0 14 2
B testCurlFailure() 0 30 6
B testGraphAPIWithOnlyParams() 0 25 1
A testLoginURLDefaults() 0 11 1
A testLoginURLDefaultsDropStateQueryParam() 0 13 1
A testLoginURLDefaultsDropCodeQueryParam() 0 13 1
A testLoginURLDefaultsDropSignedRequestParamButNotOthers() 0 14 1
A testLoginURLCustomNext() 0 19 1
A testLogoutURLDefaults() 0 12 1
A testLoginStatusURLDefaults() 0 11 1
A testLoginStatusURLCustom() 0 18 1
A testNonDefaultPort() 0 11 1
A testSecureCurrentUrl() 0 12 1
A testSecureCurrentUrlWithNonDefaultPort() 0 12 1
A testBase64UrlEncode() 0 6 1
A testSignedToken() 0 12 1
A testNonTossedSignedtoken() 0 13 1
A testSignedRequestWithEmptyValue() 0 11 1
A testSignedRequestWithWrongAlgo() 0 9 1
A testMakeAndParse() 0 10 1
A testMakeSignedRequestExpectsArray() 0 7 1
A testBundledCACert() 0 15 1
A testVideoUpload() 0 10 1
A testVideoUploadGraph() 0 10 1
A testGetUserAndAccessTokenFromSession() 0 16 1
A testGetUserAndAccessTokenFromSignedRequestNotSession() 0 22 1
A testGetUserWithoutCodeOrSignedRequestOrSession() 0 19 1
A testGetAccessTokenUsingCodeInJsSdkCookie() 0 20 1
A testSignedRequestWithoutAuthClearsData() 0 17 1
B testInvalidCodeInSignedRequestWillClearData() 0 26 1
B testInvalidCodeWillClearData() 0 26 1
B testValidCodeToToken() 0 24 1
A testSignedRequestWithoutAuthClearsDataInAvailData() 0 17 1
B testFailedToGetUserFromAccessTokenClearsData() 0 24 1
B testUserFromAccessTokenIsStored() 0 26 1
A testUserFromAccessTokenPullsID() 0 22 1
B testUserFromAccessTokenResetsOnApiException() 0 25 1
A testEmptyCodeReturnsFalse() 0 9 1
A testNullRedirectURIUsesCurrentURL() 0 22 1
A testNullRedirectURIAllowsEmptyStringForCookie() 0 22 1
A testAPIExceptionDuringCodeExchangeIsIgnored() 0 16 1
A testEmptyResponseInCodeExchangeIsIgnored() 0 16 1
A testExistingStateRestoredInConstructor() 0 7 1
A testMissingAccessTokenInCodeExchangeIsIgnored() 0 16 1
A testAppsecretProofNoParams() 0 12 1
A testAppsecretProofWithParams() 0 11 1
A testExceptionConstructorWithErrorCode() 0 5 1
A testExceptionConstructorWithInvalidErrorCode() 0 4 1
A testExceptionTypeFalse() 0 4 1
A testExceptionTypeMixedDraft00() 0 4 1
A testExceptionTypeDraft00() 0 6 1
A testExceptionTypeDraft10() 0 5 1
A testExceptionTypeDefault() 0 4 1
A testExceptionToString() 0 7 1
A testDestroyClearsCookie() 0 12 1
B testAuthExpireSessionDestroysSession() 0 25 1
B testLowercaseAuthRevokeAuthDestroysSession() 0 25 1
A testErrorCodeFromRestAPIThrowsException() 0 16 1
A testJsonEncodeOfNonStringParams() 0 14 1
A testSessionBackedFacebook() 0 17 1
A testSessionBackedFacebookIgnoresUnsupportedKey() 0 16 1
B testClearSessionBackedFacebook() 0 25 1
A testSessionBackedFacebookIgnoresUnsupportedKeyInClear() 0 13 1
A testClearAllSessionBackedFacebook() 0 15 1
A testSharedSessionBackedFacebook() 0 19 1
A testSharedSessionBackedFacebookIgnoresUnsupportedKey() 0 19 1
A testSharedClearSessionBackedFacebook() 0 22 1
A testSharedSessionBackedFacebookIgnoresUnsupportedKeyInClear() 0 20 1
A testSharedClearAllSessionBackedFacebook() 0 22 1
B testSharedSessionBackedFacebookIsRestored() 0 32 1
B testSharedSessionBackedFacebookIsNotRestoredWhenCorrupt() 0 36 1
A testHttpHost() 0 10 1
A testHttpProtocol() 0 9 1
A testHttpHostForwarded() 0 11 1
A testHttpProtocolForwarded() 0 10 1
A testHttpProtocolForwardedSecure() 0 10 1
A testEndsWith() 0 6 1
A provideEndsWith() 0 12 1
A testIsAllowedDomain() 0 6 1
A provideIsAllowedDomain() 0 9 1
A generateMD5HashOfRandomValue() 0 3 1
A setUp() 0 3 1
A tearDown() 0 4 1
A clearSuperGlobals() 0 13 2
A assertIsSubset() 0 7 3

How to fix   Complexity   

Complex Class

Complex classes like PHPSDKTestCase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHPSDKTestCase, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright 2011 Facebook, Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6
 * not use this file except in compliance with the License. You may obtain
7
 * a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
 * License for the specific language governing permissions and limitations
15
 * under the License.
16
 */
17
18
class PHPSDKTestCase extends PHPUnit_Framework_TestCase {
19
  const APP_ID = '117743971608120';
20
  const SECRET = '9c8ea2071859659bea1246d33a9207cf';
21
22
  const MIGRATED_APP_ID = '174236045938435';
23
  const MIGRATED_SECRET = '0073dce2d95c4a5c2922d1827ea0cca6';
24
25
  const TEST_USER   = 499834690;
26
  const TEST_USER_2 = 499835484;
27
28
  private static $kExpiredAccessToken = 'AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9jLuWsZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ';
29
30
  private static function kValidSignedRequest($id = self::TEST_USER, $oauth_token = null) {
31
    $facebook = new FBPublic(array(
32
      'appId'  => self::APP_ID,
33
      'secret' => self::SECRET,
34
    ));
35
    return $facebook->publicMakeSignedRequest(
36
      array(
37
        'user_id' => $id,
38
        'oauth_token' => $oauth_token
39
      )
40
    );
41
  }
42
43
  private static function kNonTosedSignedRequest() {
44
    $facebook = new FBPublic(array(
45
      'appId'  => self::APP_ID,
46
      'secret' => self::SECRET,
47
    ));
48
    return $facebook->publicMakeSignedRequest(array());
49
  }
50
51
  private static function kSignedRequestWithEmptyValue() {
52
    return '';
53
  }
54
55
  private static function kSignedRequestWithBogusSignature() {
56
    $facebook = new FBPublic(array(
57
      'appId'  => self::APP_ID,
58
      'secret' => 'bogus',
59
    ));
60
    return $facebook->publicMakeSignedRequest(
61
      array(
62
        'algorithm' => 'HMAC-SHA256',
63
      )
64
    );
65
  }
66
67
  private static function kSignedRequestWithWrongAlgo() {
68
    $facebook = new FBPublic(array(
69
      'appId'  => self::APP_ID,
70
      'secret' => self::SECRET,
71
    ));
72
    $data['algorithm'] = 'foo';
73
    $json = json_encode($data);
74
    $b64 = $facebook->publicBase64UrlEncode($json);
75
    $raw_sig = hash_hmac('sha256', $b64, self::SECRET, $raw = true);
76
    $sig = $facebook->publicBase64UrlEncode($raw_sig);
77
    return $sig.'.'.$b64;
78
  }
79
80
  public function testConstructor() {
81
    $facebook = new TransientFacebook(array(
82
      'appId'  => self::APP_ID,
83
      'secret' => self::SECRET,
84
    ));
85
    $this->assertEquals($facebook->getAppId(), self::APP_ID,
86
                        'Expect the App ID to be set.');
87
    $this->assertEquals($facebook->getAppSecret(), self::SECRET,
88
                        'Expect the API secret to be set.');
89
  }
90
91
  public function testConstructorWithFileUpload() {
92
    $facebook = new TransientFacebook(array(
93
      'appId'      => self::APP_ID,
94
      'secret'     => self::SECRET,
95
      'fileUpload' => true,
96
    ));
97
    $this->assertEquals($facebook->getAppId(), self::APP_ID,
98
                        'Expect the App ID to be set.');
99
    $this->assertEquals($facebook->getAppSecret(), self::SECRET,
100
                        'Expect the API secret to be set.');
101
    $this->assertTrue($facebook->getFileUploadSupport(),
102
                      'Expect file upload support to be on.');
103
    // alias (depricated) for getFileUploadSupport -- test until removed
104
    $this->assertTrue($facebook->useFileUploadSupport(),
0 ignored issues
show
Deprecated Code introduced by
The method BaseFacebook::useFileUploadSupport() has been deprecated with message: Use getFileUploadSupport instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
105
                      'Expect file upload support to be on.');
106
  }
107
108
  public function testSetAppId() {
109
    $facebook = new TransientFacebook(array(
110
      'appId'  => self::APP_ID,
111
      'secret' => self::SECRET,
112
    ));
113
    $facebook->setAppId('dummy');
114
    $this->assertEquals($facebook->getAppId(), 'dummy',
115
                        'Expect the App ID to be dummy.');
116
  }
117
118
  public function testSetAPISecret() {
119
    $facebook = new TransientFacebook(array(
120
      'appId'  => self::APP_ID,
121
      'secret' => self::SECRET,
122
    ));
123
    $facebook->setApiSecret('dummy');
0 ignored issues
show
Deprecated Code introduced by
The method BaseFacebook::setApiSecret() has been deprecated with message: Use setAppSecret instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
124
    $this->assertEquals($facebook->getApiSecret(), 'dummy',
0 ignored issues
show
Deprecated Code introduced by
The method BaseFacebook::getApiSecret() has been deprecated with message: Use getAppSecret instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
125
                        'Expect the API secret to be dummy.');
126
  }
127
128
  public function testSetAPPSecret() {
129
    $facebook = new TransientFacebook(array(
130
      'appId'  => self::APP_ID,
131
      'secret' => self::SECRET,
132
    ));
133
    $facebook->setAppSecret('dummy');
134
    $this->assertEquals($facebook->getAppSecret(), 'dummy',
135
                        'Expect the API secret to be dummy.');
136
  }
137
138
  public function testSetAccessToken() {
139
    $facebook = new TransientFacebook(array(
140
      'appId'  => self::APP_ID,
141
      'secret' => self::SECRET,
142
    ));
143
144
    $facebook->setAccessToken('saltydog');
145
    $this->assertEquals($facebook->getAccessToken(), 'saltydog',
146
                        'Expect installed access token to remain \'saltydog\'');
147
  }
148
149
  public function testSetFileUploadSupport() {
150
    $facebook = new TransientFacebook(array(
151
      'appId'  => self::APP_ID,
152
      'secret' => self::SECRET,
153
    ));
154
    $this->assertFalse($facebook->getFileUploadSupport(),
155
                       'Expect file upload support to be off.');
156
    // alias for getFileUploadSupport (depricated), testing until removed
157
    $this->assertFalse($facebook->useFileUploadSupport(),
0 ignored issues
show
Deprecated Code introduced by
The method BaseFacebook::useFileUploadSupport() has been deprecated with message: Use getFileUploadSupport instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
158
                       'Expect file upload support to be off.');
159
    $facebook->setFileUploadSupport(true);
160
    $this->assertTrue($facebook->getFileUploadSupport(),
161
                      'Expect file upload support to be on.');
162
    // alias for getFileUploadSupport (depricated), testing until removed
163
    $this->assertTrue($facebook->useFileUploadSupport(),
0 ignored issues
show
Deprecated Code introduced by
The method BaseFacebook::useFileUploadSupport() has been deprecated with message: Use getFileUploadSupport instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
164
                      'Expect file upload support to be on.');
165
  }
166
167
  public function testGetCurrentURL() {
168
    $facebook = new FBGetCurrentURLFacebook(array(
169
      'appId'  => self::APP_ID,
170
      'secret' => self::SECRET,
171
    ));
172
173
    // fake the HPHP $_SERVER globals
174
    $_SERVER['HTTP_HOST'] = 'www.test.com';
175
    $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=one&two=two&three=three';
176
    $current_url = $facebook->publicGetCurrentUrl();
177
    $this->assertEquals(
178
      'http://www.test.com/unit-tests.php?one=one&two=two&three=three',
179
      $current_url,
180
      'getCurrentUrl function is changing the current URL');
181
182
    // ensure structure of valueless GET params is retained (sometimes
183
    // an = sign was present, and sometimes it was not)
184
    // first test when equal signs are present
185
    $_SERVER['HTTP_HOST'] = 'www.test.com';
186
    $_SERVER['REQUEST_URI'] = '/unit-tests.php?one=&two=&three=';
187
    $current_url = $facebook->publicGetCurrentUrl();
188
    $this->assertEquals(
189
      'http://www.test.com/unit-tests.php?one=&two=&three=',
190
      $current_url,
191
      'getCurrentUrl function is changing the current URL');
192
193
    // now confirm that
194
    $_SERVER['HTTP_HOST'] = 'www.test.com';
195
    $_SERVER['REQUEST_URI'] = '/unit-tests.php?one&two&three';
196
    $current_url = $facebook->publicGetCurrentUrl();
197
    $this->assertEquals(
198
      'http://www.test.com/unit-tests.php?one&two&three',
199
      $current_url,
200
      'getCurrentUrl function is changing the current URL');
201
  }
202
203
  public function testGetLoginURL() {
204
    $facebook = new Facebook(array(
205
      'appId'  => self::APP_ID,
206
      'secret' => self::SECRET,
207
    ));
208
209
    // fake the HPHP $_SERVER globals
210
    $_SERVER['HTTP_HOST'] = 'www.test.com';
211
    $_SERVER['REQUEST_URI'] = '/unit-tests.php';
212
    $login_url = parse_url($facebook->getLoginUrl());
213
    $this->assertEquals($login_url['scheme'], 'https');
214
    $this->assertEquals($login_url['host'], 'www.facebook.com');
215
    $this->assertEquals($login_url['path'], '/dialog/oauth');
216
    $expected_login_params =
217
      array('client_id' => self::APP_ID,
218
            'redirect_uri' => 'http://www.test.com/unit-tests.php');
219
220
    $query_map = array();
221
    parse_str($login_url['query'], $query_map);
222
    $this->assertIsSubset($expected_login_params, $query_map);
223
    // we don't know what the state is, but we know it's an md5 and should
224
    // be 32 characters long.
225
    $this->assertEquals(strlen($query_map['state']), $num_characters = 32);
226
  }
227
228
  public function testGetLoginURLWithExtraParams() {
229
    $facebook = new Facebook(array(
230
      'appId'  => self::APP_ID,
231
      'secret' => self::SECRET,
232
    ));
233
234
    // fake the HPHP $_SERVER globals
235
    $_SERVER['HTTP_HOST'] = 'www.test.com';
236
    $_SERVER['REQUEST_URI'] = '/unit-tests.php';
237
    $extra_params = array('scope' => 'email, sms',
238
                          'nonsense' => 'nonsense');
239
    $login_url = parse_url($facebook->getLoginUrl($extra_params));
240
    $this->assertEquals($login_url['scheme'], 'https');
241
    $this->assertEquals($login_url['host'], 'www.facebook.com');
242
    $this->assertEquals($login_url['path'], '/dialog/oauth');
243
    $expected_login_params =
244
      array_merge(
245
        array('client_id' => self::APP_ID,
246
              'redirect_uri' => 'http://www.test.com/unit-tests.php'),
247
        $extra_params);
248
    $query_map = array();
249
    parse_str($login_url['query'], $query_map);
250
    $this->assertIsSubset($expected_login_params, $query_map);
251
    // we don't know what the state is, but we know it's an md5 and should
252
    // be 32 characters long.
253
    $this->assertEquals(strlen($query_map['state']), $num_characters = 32);
254
  }
255
256
  public function testGetLoginURLWithScopeParamsAsArray() {
257
    $facebook = new Facebook(array(
258
      'appId'  => self::APP_ID,
259
      'secret' => self::SECRET,
260
    ));
261
262
    // fake the HPHP $_SERVER globals
263
    $_SERVER['HTTP_HOST'] = 'www.test.com';
264
    $_SERVER['REQUEST_URI'] = '/unit-tests.php';
265
    $scope_params_as_array = array('email','sms','read_stream');
266
    $extra_params = array('scope' => $scope_params_as_array,
267
                          'nonsense' => 'nonsense');
268
    $login_url = parse_url($facebook->getLoginUrl($extra_params));
269
    $this->assertEquals($login_url['scheme'], 'https');
270
    $this->assertEquals($login_url['host'], 'www.facebook.com');
271
    $this->assertEquals($login_url['path'], '/dialog/oauth');
272
    // expect api to flatten array params to comma separated list
273
    // should do the same here before asserting to make sure API is behaving
274
    // correctly;
275
    $extra_params['scope'] = implode(',', $scope_params_as_array);
276
    $expected_login_params =
277
      array_merge(
278
        array('client_id' => self::APP_ID,
279
              'redirect_uri' => 'http://www.test.com/unit-tests.php'),
280
        $extra_params);
281
    $query_map = array();
282
    parse_str($login_url['query'], $query_map);
283
    $this->assertIsSubset($expected_login_params, $query_map);
284
    // we don't know what the state is, but we know it's an md5 and should
285
    // be 32 characters long.
286
    $this->assertEquals(strlen($query_map['state']), $num_characters = 32);
287
  }
288
289
  public function testGetCodeWithValidCSRFState() {
290
    $facebook = new FBCode(array(
291
      'appId'  => self::APP_ID,
292
      'secret' => self::SECRET,
293
    ));
294
295
    $facebook->setCSRFStateToken();
296
    $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue();
297
    $_REQUEST['state'] = $facebook->getCSRFStateToken();
298
    $this->assertEquals($code,
299
                        $facebook->publicGetCode(),
300
                        'Expect code to be pulled from $_REQUEST[\'code\']');
301
  }
302
303
  public function testGetCodeWithInvalidCSRFState() {
304
    $facebook = new FBCode(array(
305
      'appId'  => self::APP_ID,
306
      'secret' => self::SECRET,
307
    ));
308
309
    $facebook->setCSRFStateToken();
310
    $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue();
311
    $_REQUEST['state'] = $facebook->getCSRFStateToken().'forgery!!!';
312
    $this->assertFalse($facebook->publicGetCode(),
313
                       'Expect getCode to fail, CSRF state should not match.');
314
  }
315
316
  public function testGetCodeWithMissingCSRFState() {
317
    $facebook = new FBCode(array(
318
      'appId'  => self::APP_ID,
319
      'secret' => self::SECRET,
320
    ));
321
322
    $code = $_REQUEST['code'] = $this->generateMD5HashOfRandomValue();
323
    // intentionally don't set CSRF token at all
324
    $this->assertFalse($facebook->publicGetCode(),
325
                       'Expect getCode to fail, CSRF state not sent back.');
326
  }
327
328
  public function testPersistentCSRFState()
329
  {
330
    $facebook = new FBCode(array(
331
      'appId'  => self::APP_ID,
332
      'secret' => self::SECRET,
333
    ));
334
    $facebook->setCSRFStateToken();
335
    $code = $facebook->getCSRFStateToken();
336
337
    $facebook = new FBCode(array(
338
      'appId'  => self::APP_ID,
339
      'secret' => self::SECRET,
340
    ));
341
342
    $this->assertEquals($code, $facebook->publicGetState(),
343
            'Persisted CSRF state token not loaded correctly');
344
  }
345
346
  public function testPersistentCSRFStateWithSharedSession()
347
  {
348
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
349
    $facebook = new FBCode(array(
350
      'appId'  => self::APP_ID,
351
      'secret' => self::SECRET,
352
      'sharedSession' => true,
353
    ));
354
    $facebook->setCSRFStateToken();
355
    $code = $facebook->getCSRFStateToken();
356
357
    $facebook = new FBCode(array(
358
      'appId'  => self::APP_ID,
359
      'secret' => self::SECRET,
360
      'sharedSession' => true,
361
    ));
362
363
    $this->assertEquals($code, $facebook->publicGetState(),
364
            'Persisted CSRF state token not loaded correctly with shared session');
365
  }
366
367
  public function testGetUserFromSignedRequest() {
368
    $facebook = new TransientFacebook(array(
369
      'appId'  => self::APP_ID,
370
      'secret' => self::SECRET,
371
    ));
372
373
    $_REQUEST['signed_request'] = self::kValidSignedRequest();
374
    $this->assertEquals('499834690', $facebook->getUser(),
375
                        'Failed to get user ID from a valid signed request.');
376
  }
377
378
  public function testDisallowSignedRequest() {
379
    $facebook = new TransientFacebook(array(
380
      'appId'  => self::APP_ID,
381
      'secret' => self::SECRET,
382
      'allowSignedRequest' => false
383
    ));
384
385
    $_REQUEST['signed_request'] = self::kValidSignedRequest();
386
    $this->assertEquals(0, $facebook->getUser(),
387
        'Should not have received valid user from signed_request.');
388
  }
389
390
391
    public function testSignedRequestRewrite(){
392
    $facebook = new FBRewrite(array(
393
      'appId'  => self::APP_ID,
394
      'secret' => self::SECRET,
395
    ));
396
397
    $_REQUEST['signed_request'] = self::kValidSignedRequest(self::TEST_USER, 'Hello sweetie');
398
399
    $this->assertEquals(self::TEST_USER, $facebook->getUser(),
400
                        'Failed to get user ID from a valid signed request.');
401
402
    $this->assertEquals('Hello sweetie', $facebook->getAccessToken(),
403
                        'Failed to get access token from signed request');
404
405
    $facebook->uncache();
406
407
    $_REQUEST['signed_request'] = self::kValidSignedRequest(self::TEST_USER_2, 'spoilers');
408
409
    $this->assertEquals(self::TEST_USER_2, $facebook->getUser(),
410
                        'Failed to get user ID from a valid signed request.');
411
412
    $_REQUEST['signed_request'] = null;
413
    $facebook ->uncacheSignedRequest();
414
415
    $this->assertNotEquals('Hello sweetie', $facebook->getAccessToken(),
416
                        'Failed to clear access token');
417
  }
418
419
  public function testGetSignedRequestFromCookie() {
420
    $facebook = new FBPublicCookie(array(
421
      'appId'  => self::APP_ID,
422
      'secret' => self::SECRET,
423
    ));
424
425
    $_COOKIE[$facebook->publicGetSignedRequestCookieName()] =
426
      self::kValidSignedRequest();
427
    $this->assertNotNull($facebook->publicGetSignedRequest());
428
    $this->assertEquals('499834690', $facebook->getUser(),
429
                        'Failed to get user ID from a valid signed request.');
430
  }
431
432
  public function testGetSignedRequestWithIncorrectSignature() {
433
    $facebook = new FBPublicCookie(array(
434
      'appId'  => self::APP_ID,
435
      'secret' => self::SECRET,
436
    ));
437
438
    $_COOKIE[$facebook->publicGetSignedRequestCookieName()] =
439
      self::kSignedRequestWithBogusSignature();
440
    $this->assertNull($facebook->publicGetSignedRequest());
441
  }
442
443
  public function testNonUserAccessToken() {
444
    $facebook = new FBAccessToken(array(
445
      'appId'  => self::APP_ID,
446
      'secret' => self::SECRET,
447
    ));
448
449
    // no cookies, and no request params, so no user or code,
450
    // so no user access token (even with cookie support)
451
    $this->assertEquals($facebook->publicGetApplicationAccessToken(),
452
                        $facebook->getAccessToken(),
453
                        'Access token should be that for logged out users.');
454
  }
455
456
  public function testMissingMetadataCookie() {
457
    $fb = new FBPublicCookie(array(
458
      'appId'  => self::APP_ID,
459
      'secret' => self::SECRET,
460
    ));
461
    $this->assertEmpty($fb->publicGetMetadataCookie());
462
  }
463
464
  public function testEmptyMetadataCookie() {
465
    $fb = new FBPublicCookie(array(
466
      'appId'  => self::APP_ID,
467
      'secret' => self::SECRET,
468
    ));
469
    $_COOKIE[$fb->publicGetMetadataCookieName()] = '';
470
    $this->assertEmpty($fb->publicGetMetadataCookie());
471
  }
472
473
  public function testMetadataCookie() {
474
    $fb = new FBPublicCookie(array(
475
      'appId'  => self::APP_ID,
476
      'secret' => self::SECRET,
477
    ));
478
    $key = 'foo';
479
    $val = '42';
480
    $_COOKIE[$fb->publicGetMetadataCookieName()] = "$key=$val";
481
    $this->assertEquals(array($key => $val), $fb->publicGetMetadataCookie());
482
  }
483
484
  public function testQuotedMetadataCookie() {
485
    $fb = new FBPublicCookie(array(
486
      'appId'  => self::APP_ID,
487
      'secret' => self::SECRET,
488
    ));
489
    $key = 'foo';
490
    $val = '42';
491
    $_COOKIE[$fb->publicGetMetadataCookieName()] = "\"$key=$val\"";
492
    $this->assertEquals(array($key => $val), $fb->publicGetMetadataCookie());
493
  }
494
495
  public function testAPIForLoggedOutUsers() {
496
    $facebook = new TransientFacebook(array(
497
      'appId'  => self::APP_ID,
498
      'secret' => self::SECRET,
499
    ));
500
    $response = $facebook->api(array(
501
      'method' => 'fql.query',
502
      'query' => 'SELECT name FROM user WHERE uid=4',
503
    ));
504
    $this->assertEquals(count($response), 1,
505
                        'Expect one row back.');
506
    $this->assertEquals($response[0]['name'], 'Mark Zuckerberg',
507
                        'Expect the name back.');
508
  }
509
510
  public function testAPIWithBogusAccessToken() {
511
    $facebook = new TransientFacebook(array(
512
      'appId'  => self::APP_ID,
513
      'secret' => self::SECRET,
514
    ));
515
516
    $facebook->setAccessToken('this-is-not-really-an-access-token');
517
    // if we don't set an access token and there's no way to
518
    // get one, then the FQL query below works beautifully, handing
519
    // over Zuck's public data.  But if you specify a bogus access
520
    // token as I have right here, then the FQL query should fail.
521
    // We could return just Zuck's public data, but that wouldn't
522
    // advertise the issue that the access token is at worst broken
523
    // and at best expired.
524
    try {
525
      $response = $facebook->api(array(
526
        'method' => 'fql.query',
527
        'query' => 'SELECT name FROM profile WHERE id=4',
528
      ));
529
      $this->fail('Should not get here.');
530
    } catch(FacebookApiException $e) {
531
      $result = $e->getResult();
532
      $this->assertTrue(is_array($result), 'expect a result object');
533
      $this->assertEquals('190', $result['error_code'], 'expect code');
534
    }
535
  }
536
537
  public function testAPIGraphPublicData() {
538
    $facebook = new TransientFacebook(array(
539
      'appId'  => self::APP_ID,
540
      'secret' => self::SECRET,
541
    ));
542
543
    $response = $facebook->api('/jerry');
544
    $this->assertEquals(
545
      $response['id'], '214707', 'should get expected id.');
546
  }
547
548
  public function testGraphAPIWithBogusAccessToken() {
549
    $facebook = new TransientFacebook(array(
550
      'appId'  => self::APP_ID,
551
      'secret' => self::SECRET,
552
    ));
553
554
    $facebook->setAccessToken('this-is-not-really-an-access-token');
555
    try {
556
      $response = $facebook->api('/me');
557
      $this->fail('Should not get here.');
558
    } catch(FacebookApiException $e) {
559
      // means the server got the access token and didn't like it
560
      $msg = 'OAuthException: Invalid OAuth access token.';
561
      $this->assertEquals($msg, (string) $e,
562
                          'Expect the invalid OAuth token message.');
563
    }
564
  }
565
566
  public function testGraphAPIWithExpiredAccessToken() {
567
    $facebook = new TransientFacebook(array(
568
      'appId'  => self::APP_ID,
569
      'secret' => self::SECRET,
570
    ));
571
572
    $facebook->setAccessToken(self::$kExpiredAccessToken);
573
    try {
574
      $response = $facebook->api('/me');
575
      $this->fail('Should not get here.');
576
    } catch(FacebookApiException $e) {
577
      // means the server got the access token and didn't like it
578
      $error_msg_start = 'OAuthException: Error validating access token:';
579
      $this->assertTrue(strpos((string) $e, $error_msg_start) === 0,
580
                        'Expect the token validation error message.');
581
    }
582
  }
583
584
  public function testGraphAPIOAuthSpecError() {
585
    $facebook = new TransientFacebook(array(
586
      'appId'  => self::MIGRATED_APP_ID,
587
      'secret' => self::MIGRATED_SECRET,
588
    ));
589
590
    try {
591
      $response = $facebook->api('/me', array(
592
        'client_id' => self::MIGRATED_APP_ID));
593
594
      $this->fail('Should not get here.');
595
    } catch(FacebookApiException $e) {
596
      // means the server got the access token
597
      $msg = 'invalid_request: An active access token must be used '.
598
             'to query information about the current user.';
599
      $this->assertEquals($msg, (string) $e,
600
                          'Expect the invalid session message.');
601
    }
602
  }
603
604
  public function testGraphAPIMethodOAuthSpecError() {
605
    $facebook = new TransientFacebook(array(
606
      'appId'  => self::MIGRATED_APP_ID,
607
      'secret' => self::MIGRATED_SECRET,
608
    ));
609
610
    try {
611
      $response = $facebook->api('/daaku.shah', 'DELETE', array(
612
        'client_id' => self::MIGRATED_APP_ID));
613
      $this->fail('Should not get here.');
614
    } catch(FacebookApiException $e) {
615
      $this->assertEquals(strpos($e, 'invalid_request'), 0);
616
    }
617
  }
618
619
  public function testCurlFailure() {
620
    $facebook = new TransientFacebook(array(
621
      'appId'  => self::APP_ID,
622
      'secret' => self::SECRET,
623
    ));
624
625
    if (!defined('CURLOPT_TIMEOUT_MS')) {
626
      // can't test it if we don't have millisecond timeouts
627
      return;
628
    }
629
630
    $exception = null;
631
    try {
632
      // we dont expect facebook will ever return in 1ms
633
      Facebook::$CURL_OPTS[CURLOPT_TIMEOUT_MS] = 50;
634
      $facebook->api('/naitik');
635
    } catch(FacebookApiException $e) {
636
      $exception = $e;
637
    }
638
    unset(Facebook::$CURL_OPTS[CURLOPT_TIMEOUT_MS]);
639
    if (!$exception) {
640
      $this->fail('no exception was thrown on timeout.');
641
    }
642
643
    $code = $exception->getCode();
644
    if ($code != CURLE_OPERATION_TIMEOUTED && $code != CURLE_COULDNT_CONNECT) {
645
      $this->fail("Expected curl error code 7 or 28 but got: $code");
646
    }
647
    $this->assertEquals('CurlException', $exception->getType(), 'expect type');
648
  }
649
650
  public function testGraphAPIWithOnlyParams() {
651
    $facebook = new TransientFacebook(array(
652
      'appId'  => self::APP_ID,
653
      'secret' => self::SECRET,
654
    ));
655
656
    $response = $facebook->api('/jerry');
657
    $this->assertTrue(isset($response['id']),
658
                      'User ID should be public.');
659
    $this->assertTrue(isset($response['name']),
660
                      'User\'s name should be public.');
661
    $this->assertTrue(isset($response['first_name']),
662
                      'User\'s first name should be public.');
663
    $this->assertTrue(isset($response['last_name']),
664
                      'User\'s last name should be public.');
665
    $this->assertFalse(isset($response['work']),
666
                       'User\'s work history should only be available with '.
667
                       'a valid access token.');
668
    $this->assertFalse(isset($response['education']),
669
                       'User\'s education history should only be '.
670
                       'available with a valid access token.');
671
    $this->assertFalse(isset($response['verified']),
672
                       'User\'s verification status should only be '.
673
                       'available with a valid access token.');
674
  }
675
676
  public function testLoginURLDefaults() {
677
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
678
    $_SERVER['REQUEST_URI'] = '/examples';
679
    $facebook = new TransientFacebook(array(
680
      'appId'  => self::APP_ID,
681
      'secret' => self::SECRET,
682
    ));
683
    $encodedUrl = rawurlencode('http://fbrell.com/examples');
684
    $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl),
685
                         'Expect the current url to exist.');
686
  }
687
688
  public function testLoginURLDefaultsDropStateQueryParam() {
689
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
690
    $_SERVER['REQUEST_URI'] = '/examples?state=xx42xx';
691
    $facebook = new TransientFacebook(array(
692
      'appId'  => self::APP_ID,
693
      'secret' => self::SECRET,
694
    ));
695
    $expectEncodedUrl = rawurlencode('http://fbrell.com/examples');
696
    $this->assertTrue(strpos($facebook->getLoginUrl(), $expectEncodedUrl) > -1,
697
                      'Expect the current url to exist.');
698
    $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'),
699
                       'Expect the session param to be dropped.');
700
  }
701
702
  public function testLoginURLDefaultsDropCodeQueryParam() {
703
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
704
    $_SERVER['REQUEST_URI'] = '/examples?code=xx42xx';
705
    $facebook = new TransientFacebook(array(
706
      'appId'  => self::APP_ID,
707
      'secret' => self::SECRET,
708
    ));
709
    $expectEncodedUrl = rawurlencode('http://fbrell.com/examples');
710
    $this->assertTrue(strpos($facebook->getLoginUrl(), $expectEncodedUrl) > -1,
711
                      'Expect the current url to exist.');
712
    $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'),
713
                       'Expect the session param to be dropped.');
714
  }
715
716
  public function testLoginURLDefaultsDropSignedRequestParamButNotOthers() {
717
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
718
    $_SERVER['REQUEST_URI'] =
719
      '/examples?signed_request=xx42xx&do_not_drop=xx43xx';
720
    $facebook = new TransientFacebook(array(
721
      'appId'  => self::APP_ID,
722
      'secret' => self::SECRET,
723
    ));
724
    $expectEncodedUrl = rawurlencode('http://fbrell.com/examples');
725
    $this->assertFalse(strpos($facebook->getLoginUrl(), 'xx42xx'),
726
                       'Expect the session param to be dropped.');
727
    $this->assertTrue(strpos($facebook->getLoginUrl(), 'xx43xx') > -1,
728
                      'Expect the do_not_drop param to exist.');
729
  }
730
731
  public function testLoginURLCustomNext() {
732
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
733
    $_SERVER['REQUEST_URI'] = '/examples';
734
    $facebook = new TransientFacebook(array(
735
      'appId'  => self::APP_ID,
736
      'secret' => self::SECRET,
737
    ));
738
    $next = 'http://fbrell.com/custom';
739
    $loginUrl = $facebook->getLoginUrl(array(
740
      'redirect_uri' => $next,
741
      'cancel_url' => $next
742
    ));
743
    $currentEncodedUrl = rawurlencode('http://fbrell.com/examples');
744
    $expectedEncodedUrl = rawurlencode($next);
745
    $this->assertNotNull(strpos($loginUrl, $expectedEncodedUrl),
746
                         'Expect the custom url to exist.');
747
    $this->assertFalse(strpos($loginUrl, $currentEncodedUrl),
748
                      'Expect the current url to not exist.');
749
  }
750
751
  public function testLogoutURLDefaults() {
752
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
753
    $_SERVER['REQUEST_URI'] = '/examples';
754
    $facebook = new TransientFacebook(array(
755
      'appId'  => self::APP_ID,
756
      'secret' => self::SECRET,
757
    ));
758
    $encodedUrl = rawurlencode('http://fbrell.com/examples');
759
    $this->assertNotNull(strpos($facebook->getLogoutUrl(), $encodedUrl),
760
                         'Expect the current url to exist.');
761
    $this->assertFalse(strpos($facebook->getLogoutUrl(), self::SECRET));
762
  }
763
764
  public function testLoginStatusURLDefaults() {
765
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
766
    $_SERVER['REQUEST_URI'] = '/examples';
767
    $facebook = new TransientFacebook(array(
768
      'appId'  => self::APP_ID,
769
      'secret' => self::SECRET,
770
    ));
771
    $encodedUrl = rawurlencode('http://fbrell.com/examples');
772
    $this->assertNotNull(strpos($facebook->getLoginStatusUrl(), $encodedUrl),
773
                         'Expect the current url to exist.');
774
  }
775
776
  public function testLoginStatusURLCustom() {
777
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
778
    $_SERVER['REQUEST_URI'] = '/examples';
779
    $facebook = new TransientFacebook(array(
780
      'appId'  => self::APP_ID,
781
      'secret' => self::SECRET,
782
    ));
783
    $encodedUrl1 = rawurlencode('http://fbrell.com/examples');
784
    $okUrl = 'http://fbrell.com/here1';
785
    $encodedUrl2 = rawurlencode($okUrl);
786
    $loginStatusUrl = $facebook->getLoginStatusUrl(array(
787
      'ok_session' => $okUrl,
788
    ));
789
    $this->assertNotNull(strpos($loginStatusUrl, $encodedUrl1),
790
                         'Expect the current url to exist.');
791
    $this->assertNotNull(strpos($loginStatusUrl, $encodedUrl2),
792
                         'Expect the custom url to exist.');
793
  }
794
795
  public function testNonDefaultPort() {
796
    $_SERVER['HTTP_HOST'] = 'fbrell.com:8080';
797
    $_SERVER['REQUEST_URI'] = '/examples';
798
    $facebook = new TransientFacebook(array(
799
      'appId'  => self::APP_ID,
800
      'secret' => self::SECRET,
801
    ));
802
    $encodedUrl = rawurlencode('http://fbrell.com:8080/examples');
803
    $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl),
804
                         'Expect the current url to exist.');
805
  }
806
807
  public function testSecureCurrentUrl() {
808
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
809
    $_SERVER['REQUEST_URI'] = '/examples';
810
    $_SERVER['HTTPS'] = 'on';
811
    $facebook = new TransientFacebook(array(
812
      'appId'  => self::APP_ID,
813
      'secret' => self::SECRET,
814
    ));
815
    $encodedUrl = rawurlencode('https://fbrell.com/examples');
816
    $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl),
817
                         'Expect the current url to exist.');
818
  }
819
820
  public function testSecureCurrentUrlWithNonDefaultPort() {
821
    $_SERVER['HTTP_HOST'] = 'fbrell.com:8080';
822
    $_SERVER['REQUEST_URI'] = '/examples';
823
    $_SERVER['HTTPS'] = 'on';
824
    $facebook = new TransientFacebook(array(
825
      'appId'  => self::APP_ID,
826
      'secret' => self::SECRET,
827
    ));
828
    $encodedUrl = rawurlencode('https://fbrell.com:8080/examples');
829
    $this->assertNotNull(strpos($facebook->getLoginUrl(), $encodedUrl),
830
                         'Expect the current url to exist.');
831
  }
832
833
  public function testBase64UrlEncode() {
834
    $input = 'Facebook rocks';
835
    $output = 'RmFjZWJvb2sgcm9ja3M';
836
837
    $this->assertEquals(FBPublic::publicBase64UrlDecode($output), $input);
838
  }
839
840
  public function testSignedToken() {
841
    $facebook = new FBPublic(array(
842
      'appId'  => self::APP_ID,
843
      'secret' => self::SECRET
844
    ));
845
    $sr = self::kValidSignedRequest();
846
    $payload = $facebook->publicParseSignedRequest($sr);
847
    $this->assertNotNull($payload, 'Expected token to parse');
848
    $this->assertEquals($facebook->getSignedRequest(), null);
849
    $_REQUEST['signed_request'] = $sr;
850
    $this->assertEquals($facebook->getSignedRequest(), $payload);
851
  }
852
853
  public function testNonTossedSignedtoken() {
854
    $facebook = new FBPublic(array(
855
      'appId'  => self::APP_ID,
856
      'secret' => self::SECRET
857
    ));
858
    $payload = $facebook->publicParseSignedRequest(
859
      self::kNonTosedSignedRequest());
860
    $this->assertNotNull($payload, 'Expected token to parse');
861
    $this->assertNull($facebook->getSignedRequest());
862
    $_REQUEST['signed_request'] = self::kNonTosedSignedRequest();
863
    $sr = $facebook->getSignedRequest();
864
    $this->assertTrue(isset($sr['algorithm']));
865
  }
866
867
  public function testSignedRequestWithEmptyValue() {
868
    $fb = new FBPublicCookie(array(
869
      'appId'  => self::APP_ID,
870
      'secret' => self::SECRET
871
    ));
872
    $_REQUEST['signed_request'] = self::kSignedRequestWithEmptyValue();
873
    $this->assertNull($fb->getSignedRequest());
874
    $_COOKIE[$fb->publicGetSignedRequestCookieName()] =
875
      self::kSignedRequestWithEmptyValue();
876
    $this->assertNull($fb->getSignedRequest());
877
  }
878
879
  public function testSignedRequestWithWrongAlgo() {
880
    $fb = new FBPublic(array(
881
      'appId'  => self::APP_ID,
882
      'secret' => self::SECRET
883
    ));
884
    $payload = $fb->publicParseSignedRequest(
885
      self::kSignedRequestWithWrongAlgo());
886
    $this->assertNull($payload, 'Expected nothing back.');
887
  }
888
889
  public function testMakeAndParse() {
890
    $fb = new FBPublic(array(
891
      'appId'  => self::APP_ID,
892
      'secret' => self::SECRET
893
    ));
894
    $data = array('foo' => 42);
895
    $sr = $fb->publicMakeSignedRequest($data);
896
    $decoded = $fb->publicParseSignedRequest($sr);
897
    $this->assertEquals($data['foo'], $decoded['foo']);
898
  }
899
900
  /**
901
   * @expectedException InvalidArgumentException
902
   */
903
  public function testMakeSignedRequestExpectsArray() {
904
    $fb = new FBPublic(array(
905
      'appId'  => self::APP_ID,
906
      'secret' => self::SECRET
907
    ));
908
    $sr = $fb->publicMakeSignedRequest('');
909
  }
910
911
  public function testBundledCACert() {
912
    $facebook = new TransientFacebook(array(
913
      'appId'  => self::APP_ID,
914
      'secret' => self::SECRET
915
    ));
916
917
      // use the bundled cert from the start
918
    Facebook::$CURL_OPTS[CURLOPT_CAINFO] =
919
      dirname(__FILE__) . '/../src/fb_ca_chain_bundle.crt';
920
    $response = $facebook->api('/naitik');
921
922
    unset(Facebook::$CURL_OPTS[CURLOPT_CAINFO]);
923
    $this->assertEquals(
924
      $response['id'], '5526183', 'should get expected id.');
925
  }
926
927
  public function testVideoUpload() {
928
    $facebook = new FBRecordURL(array(
929
      'appId'  => self::APP_ID,
930
      'secret' => self::SECRET
931
    ));
932
933
    $facebook->api(array('method' => 'video.upload'));
934
    $this->assertContains('//api-video.', $facebook->getRequestedURL(),
935
                          'video.upload should go against api-video');
936
  }
937
938
  public function testVideoUploadGraph() {
939
    $facebook = new FBRecordURL(array(
940
      'appId'  => self::APP_ID,
941
      'secret' => self::SECRET
942
    ));
943
944
    $facebook->api('/me/videos', 'POST');
945
    $this->assertContains('//graph-video.', $facebook->getRequestedURL(),
946
                          '/me/videos should go against graph-video');
947
  }
948
949
  public function testGetUserAndAccessTokenFromSession() {
950
    $facebook = new PersistentFBPublic(array(
951
                                         'appId'  => self::APP_ID,
952
                                         'secret' => self::SECRET
953
                                       ));
954
955
    $facebook->publicSetPersistentData('access_token',
956
                                       self::$kExpiredAccessToken);
957
    $facebook->publicSetPersistentData('user_id', 12345);
958
    $this->assertEquals(self::$kExpiredAccessToken,
959
                        $facebook->getAccessToken(),
960
                        'Get access token from persistent store.');
961
    $this->assertEquals('12345',
962
                        $facebook->getUser(),
963
                        'Get user id from persistent store.');
964
  }
965
966
  public function testGetUserAndAccessTokenFromSignedRequestNotSession() {
967
    $facebook = new PersistentFBPublic(array(
968
                                         'appId'  => self::APP_ID,
969
                                         'secret' => self::SECRET
970
                                       ));
971
972
    $_REQUEST['signed_request'] = self::kValidSignedRequest();
973
    $facebook->publicSetPersistentData('user_id', 41572);
974
    $facebook->publicSetPersistentData('access_token',
975
                                       self::$kExpiredAccessToken);
976
    $this->assertNotEquals('41572', $facebook->getUser(),
977
                           'Got user from session instead of signed request.');
978
    $this->assertEquals('499834690', $facebook->getUser(),
979
                        'Failed to get correct user ID from signed request.');
980
    $this->assertNotEquals(
981
      self::$kExpiredAccessToken,
982
      $facebook->getAccessToken(),
983
      'Got access token from session instead of signed request.');
984
    $this->assertNotEmpty(
985
      $facebook->getAccessToken(),
986
      'Failed to extract an access token from the signed request.');
987
  }
988
989
  public function testGetUserWithoutCodeOrSignedRequestOrSession() {
990
    $facebook = new PersistentFBPublic(array(
991
                                         'appId'  => self::APP_ID,
992
                                         'secret' => self::SECRET
993
                                       ));
994
995
    // deliberately leave $_REQUEST and _$SESSION empty
996
    $this->assertEmpty($_REQUEST,
997
                       'GET, POST, and COOKIE params exist even though '.
998
                       'they should.  Test cannot succeed unless all of '.
999
                       '$_REQUEST is empty.');
1000
    $this->assertEmpty($_SESSION,
1001
                       'Session is carrying state and should not be.');
1002
    $this->assertEmpty($facebook->getUser(),
1003
                       'Got a user id, even without a signed request, '.
1004
                       'access token, or session variable.');
1005
    $this->assertEmpty($_SESSION,
1006
                       'Session superglobal incorrectly populated by getUser.');
1007
  }
1008
1009
  public function testGetAccessTokenUsingCodeInJsSdkCookie() {
1010
    $code = 'code1';
1011
    $access_token = 'at1';
1012
    $methods_to_stub = array('getSignedRequest', 'getAccessTokenFromCode');
1013
    $constructor_args = array(array(
1014
      'appId'  => self::APP_ID,
1015
      'secret' => self::SECRET
1016
    ));
1017
    $stub = $this->getMock(
1018
      'TransientFacebook', $methods_to_stub, $constructor_args);
1019
    $stub
1020
      ->expects($this->once())
1021
      ->method('getSignedRequest')
1022
      ->will($this->returnValue(array('code' => $code)));
1023
    $stub
1024
      ->expects($this->once())
1025
      ->method('getAccessTokenFromCode')
1026
      ->will($this->returnValueMap(array(array($code, '', $access_token))));
1027
    $this->assertEquals($stub->getAccessToken(), $access_token);
1028
  }
1029
1030
  public function testSignedRequestWithoutAuthClearsData() {
1031
    $methods_to_stub = array('getSignedRequest', 'clearAllPersistentData');
1032
    $constructor_args = array(array(
1033
      'appId'  => self::APP_ID,
1034
      'secret' => self::SECRET
1035
    ));
1036
    $stub = $this->getMock(
1037
      'TransientFacebook', $methods_to_stub, $constructor_args);
1038
    $stub
1039
      ->expects($this->once())
1040
      ->method('getSignedRequest')
1041
      ->will($this->returnValue(array('foo' => 1)));
1042
    $stub
1043
      ->expects($this->once())
1044
      ->method('clearAllPersistentData');
1045
    $this->assertEquals(self::APP_ID.'|'.self::SECRET, $stub->getAccessToken());
1046
  }
1047
1048
  public function testInvalidCodeInSignedRequestWillClearData() {
1049
    $code = 'code1';
1050
    $methods_to_stub = array(
1051
      'getSignedRequest',
1052
      'getAccessTokenFromCode',
1053
      'clearAllPersistentData',
1054
    );
1055
    $constructor_args = array(array(
1056
      'appId'  => self::APP_ID,
1057
      'secret' => self::SECRET
1058
    ));
1059
    $stub = $this->getMock(
1060
      'TransientFacebook', $methods_to_stub, $constructor_args);
1061
    $stub
1062
      ->expects($this->once())
1063
      ->method('getSignedRequest')
1064
      ->will($this->returnValue(array('code' => $code)));
1065
    $stub
1066
      ->expects($this->once())
1067
      ->method('getAccessTokenFromCode')
1068
      ->will($this->returnValue(null));
1069
    $stub
1070
      ->expects($this->once())
1071
      ->method('clearAllPersistentData');
1072
    $this->assertEquals(self::APP_ID.'|'.self::SECRET, $stub->getAccessToken());
1073
  }
1074
1075
  public function testInvalidCodeWillClearData() {
1076
    $code = 'code1';
1077
    $methods_to_stub = array(
1078
      'getCode',
1079
      'getAccessTokenFromCode',
1080
      'clearAllPersistentData',
1081
    );
1082
    $constructor_args = array(array(
1083
      'appId'  => self::APP_ID,
1084
      'secret' => self::SECRET
1085
    ));
1086
    $stub = $this->getMock(
1087
      'TransientFacebook', $methods_to_stub, $constructor_args);
1088
    $stub
1089
      ->expects($this->once())
1090
      ->method('getCode')
1091
      ->will($this->returnValue($code));
1092
    $stub
1093
      ->expects($this->once())
1094
      ->method('getAccessTokenFromCode')
1095
      ->will($this->returnValue(null));
1096
    $stub
1097
      ->expects($this->once())
1098
      ->method('clearAllPersistentData');
1099
    $this->assertEquals(self::APP_ID.'|'.self::SECRET, $stub->getAccessToken());
1100
  }
1101
1102
  public function testValidCodeToToken() {
1103
    $code = 'code1';
1104
    $access_token = 'at1';
1105
    $methods_to_stub = array(
1106
      'getSignedRequest',
1107
      'getCode',
1108
      'getAccessTokenFromCode',
1109
    );
1110
    $constructor_args = array(array(
1111
      'appId'  => self::APP_ID,
1112
      'secret' => self::SECRET
1113
    ));
1114
    $stub = $this->getMock(
1115
      'TransientFacebook', $methods_to_stub, $constructor_args);
1116
    $stub
1117
      ->expects($this->once())
1118
      ->method('getCode')
1119
      ->will($this->returnValue($code));
1120
    $stub
1121
      ->expects($this->once())
1122
      ->method('getAccessTokenFromCode')
1123
      ->will($this->returnValueMap(array(array($code, null, $access_token))));
1124
    $this->assertEquals($stub->getAccessToken(), $access_token);
1125
  }
1126
1127
  public function testSignedRequestWithoutAuthClearsDataInAvailData() {
1128
    $methods_to_stub = array('getSignedRequest', 'clearAllPersistentData');
1129
    $constructor_args = array(array(
1130
      'appId'  => self::APP_ID,
1131
      'secret' => self::SECRET
1132
    ));
1133
    $stub = $this->getMock(
1134
      'TransientFacebook', $methods_to_stub, $constructor_args);
1135
    $stub
1136
      ->expects($this->once())
1137
      ->method('getSignedRequest')
1138
      ->will($this->returnValue(array('foo' => 1)));
1139
    $stub
1140
      ->expects($this->once())
1141
      ->method('clearAllPersistentData');
1142
    $this->assertEquals(0, $stub->getUser());
1143
  }
1144
1145
  public function testFailedToGetUserFromAccessTokenClearsData() {
1146
    $methods_to_stub = array(
1147
      'getAccessToken',
1148
      'getUserFromAccessToken',
1149
      'clearAllPersistentData',
1150
    );
1151
    $constructor_args = array(array(
1152
      'appId'  => self::APP_ID,
1153
      'secret' => self::SECRET
1154
    ));
1155
    $stub = $this->getMock(
1156
      'TransientFacebook', $methods_to_stub, $constructor_args);
1157
    $stub
1158
      ->expects($this->once())
1159
      ->method('getAccessToken')
1160
      ->will($this->returnValue('at1'));
1161
    $stub
1162
      ->expects($this->once())
1163
      ->method('getUserFromAccessToken');
1164
    $stub
1165
      ->expects($this->once())
1166
      ->method('clearAllPersistentData');
1167
    $this->assertEquals(0, $stub->getUser());
1168
  }
1169
1170
  public function testUserFromAccessTokenIsStored() {
1171
    $methods_to_stub = array(
1172
      'getAccessToken',
1173
      'getUserFromAccessToken',
1174
      'setPersistentData',
1175
    );
1176
    $constructor_args = array(array(
1177
      'appId'  => self::APP_ID,
1178
      'secret' => self::SECRET
1179
    ));
1180
    $user = 42;
1181
    $stub = $this->getMock(
1182
      'TransientFacebook', $methods_to_stub, $constructor_args);
1183
    $stub
1184
      ->expects($this->once())
1185
      ->method('getAccessToken')
1186
      ->will($this->returnValue('at1'));
1187
    $stub
1188
      ->expects($this->once())
1189
      ->method('getUserFromAccessToken')
1190
      ->will($this->returnValue($user));
1191
    $stub
1192
      ->expects($this->once())
1193
      ->method('setPersistentData');
1194
    $this->assertEquals($user, $stub->getUser());
1195
  }
1196
1197
  public function testUserFromAccessTokenPullsID() {
1198
    $methods_to_stub = array(
1199
      'getAccessToken',
1200
      'api',
1201
    );
1202
    $constructor_args = array(array(
1203
      'appId'  => self::APP_ID,
1204
      'secret' => self::SECRET
1205
    ));
1206
    $user = 42;
1207
    $stub = $this->getMock(
1208
      'TransientFacebook', $methods_to_stub, $constructor_args);
1209
    $stub
1210
      ->expects($this->once())
1211
      ->method('getAccessToken')
1212
      ->will($this->returnValue('at1'));
1213
    $stub
1214
      ->expects($this->once())
1215
      ->method('api')
1216
      ->will($this->returnValue(array('id' => $user)));
1217
    $this->assertEquals($user, $stub->getUser());
1218
  }
1219
1220
  public function testUserFromAccessTokenResetsOnApiException() {
1221
    $methods_to_stub = array(
1222
      'getAccessToken',
1223
      'clearAllPersistentData',
1224
      'api',
1225
    );
1226
    $constructor_args = array(array(
1227
      'appId'  => self::APP_ID,
1228
      'secret' => self::SECRET
1229
    ));
1230
    $stub = $this->getMock(
1231
      'TransientFacebook', $methods_to_stub, $constructor_args);
1232
    $stub
1233
      ->expects($this->once())
1234
      ->method('getAccessToken')
1235
      ->will($this->returnValue('at1'));
1236
    $stub
1237
      ->expects($this->once())
1238
      ->method('api')
1239
      ->will($this->throwException(new FacebookApiException(false)));
1240
    $stub
1241
      ->expects($this->once())
1242
      ->method('clearAllPersistentData');
1243
    $this->assertEquals(0, $stub->getUser());
1244
  }
1245
1246
  public function testEmptyCodeReturnsFalse() {
1247
    $fb = new FBPublicGetAccessTokenFromCode(array(
1248
      'appId'  => self::APP_ID,
1249
      'secret' => self::SECRET
1250
    ));
1251
    $this->assertFalse($fb->publicGetAccessTokenFromCode(''));
1252
    $this->assertFalse($fb->publicGetAccessTokenFromCode(null));
1253
    $this->assertFalse($fb->publicGetAccessTokenFromCode(false));
1254
  }
1255
1256
  public function testNullRedirectURIUsesCurrentURL() {
1257
    $methods_to_stub = array(
1258
      '_oauthRequest',
1259
      'getCurrentUrl',
1260
    );
1261
    $constructor_args = array(array(
1262
      'appId'  => self::APP_ID,
1263
      'secret' => self::SECRET
1264
    ));
1265
    $access_token = 'at1';
1266
    $stub = $this->getMock(
1267
      'FBPublicGetAccessTokenFromCode', $methods_to_stub, $constructor_args);
1268
    $stub
1269
      ->expects($this->once())
1270
      ->method('_oauthRequest')
1271
      ->will($this->returnValue("access_token=$access_token"));
1272
    $stub
1273
      ->expects($this->once())
1274
      ->method('getCurrentUrl');
1275
    $this->assertEquals(
1276
      $access_token, $stub->publicGetAccessTokenFromCode('c'));
1277
  }
1278
1279
  public function testNullRedirectURIAllowsEmptyStringForCookie() {
1280
    $methods_to_stub = array(
1281
      '_oauthRequest',
1282
      'getCurrentUrl',
1283
    );
1284
    $constructor_args = array(array(
1285
      'appId'  => self::APP_ID,
1286
      'secret' => self::SECRET
1287
    ));
1288
    $access_token = 'at1';
1289
    $stub = $this->getMock(
1290
      'FBPublicGetAccessTokenFromCode', $methods_to_stub, $constructor_args);
1291
    $stub
1292
      ->expects($this->once())
1293
      ->method('_oauthRequest')
1294
      ->will($this->returnValue("access_token=$access_token"));
1295
    $stub
1296
      ->expects($this->never())
1297
      ->method('getCurrentUrl');
1298
    $this->assertEquals(
1299
      $access_token, $stub->publicGetAccessTokenFromCode('c', ''));
1300
  }
1301
1302
  public function testAPIExceptionDuringCodeExchangeIsIgnored() {
1303
    $methods_to_stub = array(
1304
      '_oauthRequest',
1305
    );
1306
    $constructor_args = array(array(
1307
      'appId'  => self::APP_ID,
1308
      'secret' => self::SECRET
1309
    ));
1310
    $stub = $this->getMock(
1311
      'FBPublicGetAccessTokenFromCode', $methods_to_stub, $constructor_args);
1312
    $stub
1313
      ->expects($this->once())
1314
      ->method('_oauthRequest')
1315
      ->will($this->throwException(new FacebookApiException(false)));
1316
    $this->assertFalse($stub->publicGetAccessTokenFromCode('c', ''));
1317
  }
1318
1319
  public function testEmptyResponseInCodeExchangeIsIgnored() {
1320
    $methods_to_stub = array(
1321
      '_oauthRequest',
1322
    );
1323
    $constructor_args = array(array(
1324
      'appId'  => self::APP_ID,
1325
      'secret' => self::SECRET
1326
    ));
1327
    $stub = $this->getMock(
1328
      'FBPublicGetAccessTokenFromCode', $methods_to_stub, $constructor_args);
1329
    $stub
1330
      ->expects($this->once())
1331
      ->method('_oauthRequest')
1332
      ->will($this->returnValue(''));
1333
    $this->assertFalse($stub->publicGetAccessTokenFromCode('c', ''));
1334
  }
1335
1336
  public function testExistingStateRestoredInConstructor() {
1337
    $fb = new FBPublicState(array(
1338
      'appId'  => self::APP_ID,
1339
      'secret' => self::SECRET
1340
    ));
1341
    $this->assertEquals(FBPublicState::STATE, $fb->publicGetState());
1342
  }
1343
1344
  public function testMissingAccessTokenInCodeExchangeIsIgnored() {
1345
    $methods_to_stub = array(
1346
      '_oauthRequest',
1347
    );
1348
    $constructor_args = array(array(
1349
      'appId'  => self::APP_ID,
1350
      'secret' => self::SECRET
1351
    ));
1352
    $stub = $this->getMock(
1353
      'FBPublicGetAccessTokenFromCode', $methods_to_stub, $constructor_args);
1354
    $stub
1355
      ->expects($this->once())
1356
      ->method('_oauthRequest')
1357
      ->will($this->returnValue('foo=1'));
1358
    $this->assertFalse($stub->publicGetAccessTokenFromCode('c', ''));
1359
  }
1360
1361
  public function testAppsecretProofNoParams() {
1362
    $fb = new FBRecordMakeRequest(array(
1363
      'appId'  => self::APP_ID,
1364
      'secret' => self::SECRET,
1365
    ));
1366
    $token = $fb->getAccessToken();
1367
    $proof = $fb->publicGetAppSecretProof($token);
1368
    $params = array();
1369
    $fb->api('/mattynoce', $params);
1370
    $requests = $fb->publicGetRequests();
1371
    $this->assertEquals($proof, $requests[0]['params']['appsecret_proof']);
1372
  }
1373
1374
  public function testAppsecretProofWithParams() {
1375
    $fb = new FBRecordMakeRequest(array(
1376
      'appId'  => self::APP_ID,
1377
      'secret' => self::SECRET,
1378
    ));
1379
    $proof = 'foo';
1380
    $params = array('appsecret_proof' => $proof);
1381
    $fb->api('/mattynoce', $params);
1382
    $requests = $fb->publicGetRequests();
1383
    $this->assertEquals($proof, $requests[0]['params']['appsecret_proof']);
1384
  }
1385
1386
  public function testExceptionConstructorWithErrorCode() {
1387
    $code = 404;
1388
    $e = new FacebookApiException(array('error_code' => $code));
1389
    $this->assertEquals($code, $e->getCode());
1390
  }
1391
1392
  public function testExceptionConstructorWithInvalidErrorCode() {
1393
    $e = new FacebookApiException(array('error_code' => 'not an int'));
1394
    $this->assertEquals(0, $e->getCode());
1395
  }
1396
1397
  // this happens often despite the fact that it is useless
1398
  public function testExceptionTypeFalse() {
1399
    $e = new FacebookApiException(false);
1400
    $this->assertEquals('Exception', $e->getType());
1401
  }
1402
1403
  public function testExceptionTypeMixedDraft00() {
1404
    $e = new FacebookApiException(array('error' => array('message' => 'foo')));
1405
    $this->assertEquals('Exception', $e->getType());
1406
  }
1407
1408
  public function testExceptionTypeDraft00() {
1409
    $error = 'foo';
1410
    $e = new FacebookApiException(
1411
      array('error' => array('type' => $error, 'message' => 'hello world')));
1412
    $this->assertEquals($error, $e->getType());
1413
  }
1414
1415
  public function testExceptionTypeDraft10() {
1416
    $error = 'foo';
1417
    $e = new FacebookApiException(array('error' => $error));
1418
    $this->assertEquals($error, $e->getType());
1419
  }
1420
1421
  public function testExceptionTypeDefault() {
1422
    $e = new FacebookApiException(array('error' => false));
1423
    $this->assertEquals('Exception', $e->getType());
1424
  }
1425
1426
  public function testExceptionToString() {
1427
    $e = new FacebookApiException(array(
1428
      'error_code' => 1,
1429
      'error_description' => 'foo',
1430
    ));
1431
    $this->assertEquals('Exception: 1: foo', (string) $e);
1432
  }
1433
1434
  public function testDestroyClearsCookie() {
1435
    $fb = new FBPublicCookie(array(
1436
      'appId'  => self::APP_ID,
1437
      'secret' => self::SECRET,
1438
    ));
1439
    $_COOKIE[$fb->publicGetSignedRequestCookieName()] = 'foo';
1440
    $_COOKIE[$fb->publicGetMetadataCookieName()] = 'base_domain=fbrell.com';
1441
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1442
    $fb->destroySession();
1443
    $this->assertFalse(
1444
      array_key_exists($fb->publicGetSignedRequestCookieName(), $_COOKIE));
1445
  }
1446
1447
  public function testAuthExpireSessionDestroysSession() {
1448
    $methods_to_stub = array(
1449
      '_oauthRequest',
1450
      'destroySession',
1451
    );
1452
    $constructor_args = array(array(
1453
      'appId'  => self::APP_ID,
1454
      'secret' => self::SECRET
1455
    ));
1456
    $key = 'foo';
1457
    $val = 42;
1458
    $stub = $this->getMock(
1459
      'TransientFacebook', $methods_to_stub, $constructor_args);
1460
    $stub
1461
      ->expects($this->once())
1462
      ->method('_oauthRequest')
1463
      ->will($this->returnValue("{\"$key\":$val}"));
1464
    $stub
1465
      ->expects($this->once())
1466
      ->method('destroySession');
1467
    $this->assertEquals(
1468
      array($key => $val),
1469
      $stub->api(array('method' => 'auth.expireSession'))
1470
    );
1471
  }
1472
1473
  public function testLowercaseAuthRevokeAuthDestroysSession() {
1474
    $methods_to_stub = array(
1475
      '_oauthRequest',
1476
      'destroySession',
1477
    );
1478
    $constructor_args = array(array(
1479
      'appId'  => self::APP_ID,
1480
      'secret' => self::SECRET
1481
    ));
1482
    $key = 'foo';
1483
    $val = 42;
1484
    $stub = $this->getMock(
1485
      'TransientFacebook', $methods_to_stub, $constructor_args);
1486
    $stub
1487
      ->expects($this->once())
1488
      ->method('_oauthRequest')
1489
      ->will($this->returnValue("{\"$key\":$val}"));
1490
    $stub
1491
      ->expects($this->once())
1492
      ->method('destroySession');
1493
    $this->assertEquals(
1494
      array($key => $val),
1495
      $stub->api(array('method' => 'auth.revokeauthorization'))
1496
    );
1497
  }
1498
1499
  /**
1500
   * @expectedException FacebookAPIException
1501
   */
1502
  public function testErrorCodeFromRestAPIThrowsException() {
1503
    $methods_to_stub = array(
1504
      '_oauthRequest',
1505
    );
1506
    $constructor_args = array(array(
1507
      'appId'  => self::APP_ID,
1508
      'secret' => self::SECRET
1509
    ));
1510
    $stub = $this->getMock(
1511
      'TransientFacebook', $methods_to_stub, $constructor_args);
1512
    $stub
1513
      ->expects($this->once())
1514
      ->method('_oauthRequest')
1515
      ->will($this->returnValue('{"error_code": 500}'));
1516
    $stub->api(array('method' => 'foo'));
1517
  }
1518
1519
  public function testJsonEncodeOfNonStringParams() {
1520
    $foo = array(1, 2);
1521
    $params = array(
1522
      'method' => 'get',
1523
      'foo' => $foo,
1524
    );
1525
    $fb = new FBRecordMakeRequest(array(
1526
      'appId'  => self::APP_ID,
1527
      'secret' => self::SECRET,
1528
    ));
1529
    $fb->api('/naitik', $params);
1530
    $requests = $fb->publicGetRequests();
1531
    $this->assertEquals(json_encode($foo), $requests[0]['params']['foo']);
1532
  }
1533
1534
  public function testSessionBackedFacebook() {
1535
    $fb = new PersistentFBPublic(array(
1536
      'appId'  => self::APP_ID,
1537
      'secret' => self::SECRET,
1538
    ));
1539
    $key = 'state';
1540
    $val = 'foo';
1541
    $fb->publicSetPersistentData($key, $val);
1542
    $this->assertEquals(
1543
      $val,
1544
      $_SESSION[sprintf('fb_%s_%s', self::APP_ID, $key)]
1545
    );
1546
    $this->assertEquals(
1547
      $val,
1548
      $fb->publicGetPersistentData($key)
1549
    );
1550
  }
1551
1552
  public function testSessionBackedFacebookIgnoresUnsupportedKey() {
1553
    $fb = new PersistentFBPublic(array(
1554
      'appId'  => self::APP_ID,
1555
      'secret' => self::SECRET,
1556
    ));
1557
    $key = '--invalid--';
1558
    $val = 'foo';
1559
    $fb->publicSetPersistentData($key, $val);
1560
    $this->assertFalse(
1561
      array_key_exists(
1562
        sprintf('fb_%s_%s', self::APP_ID, $key),
1563
        $_SESSION
1564
      )
1565
    );
1566
    $this->assertFalse($fb->publicGetPersistentData($key));
1567
  }
1568
1569
  public function testClearSessionBackedFacebook() {
1570
    $fb = new PersistentFBPublic(array(
1571
      'appId'  => self::APP_ID,
1572
      'secret' => self::SECRET,
1573
    ));
1574
    $key = 'state';
1575
    $val = 'foo';
1576
    $fb->publicSetPersistentData($key, $val);
1577
    $this->assertEquals(
1578
      $val,
1579
      $_SESSION[sprintf('fb_%s_%s', self::APP_ID, $key)]
1580
    );
1581
    $this->assertEquals(
1582
      $val,
1583
      $fb->publicGetPersistentData($key)
1584
    );
1585
    $fb->publicClearPersistentData($key);
1586
    $this->assertFalse(
1587
      array_key_exists(
1588
        sprintf('fb_%s_%s', self::APP_ID, $key),
1589
        $_SESSION
1590
      )
1591
    );
1592
    $this->assertFalse($fb->publicGetPersistentData($key));
1593
  }
1594
1595
  public function testSessionBackedFacebookIgnoresUnsupportedKeyInClear() {
1596
    $fb = new PersistentFBPublic(array(
1597
      'appId'  => self::APP_ID,
1598
      'secret' => self::SECRET,
1599
    ));
1600
    $key = '--invalid--';
1601
    $val = 'foo';
1602
    $session_var_name = sprintf('fb_%s_%s', self::APP_ID, $key);
1603
    $_SESSION[$session_var_name] = $val;
1604
    $fb->publicClearPersistentData($key);
1605
    $this->assertTrue(array_key_exists($session_var_name, $_SESSION));
1606
    $this->assertFalse($fb->publicGetPersistentData($key));
1607
  }
1608
1609
  public function testClearAllSessionBackedFacebook() {
1610
    $fb = new PersistentFBPublic(array(
1611
      'appId'  => self::APP_ID,
1612
      'secret' => self::SECRET,
1613
    ));
1614
    $key = 'state';
1615
    $val = 'foo';
1616
    $session_var_name = sprintf('fb_%s_%s', self::APP_ID, $key);
1617
    $fb->publicSetPersistentData($key, $val);
1618
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1619
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1620
    $fb->publicClearAllPersistentData();
1621
    $this->assertFalse(array_key_exists($session_var_name, $_SESSION));
1622
    $this->assertFalse($fb->publicGetPersistentData($key));
1623
  }
1624
1625
  public function testSharedSessionBackedFacebook() {
1626
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1627
    $fb = new PersistentFBPublic(array(
1628
      'appId'  => self::APP_ID,
1629
      'secret' => self::SECRET,
1630
      'sharedSession' => true,
1631
    ));
1632
    $key = 'state';
1633
    $val = 'foo';
1634
    $session_var_name = sprintf(
1635
      '%s_fb_%s_%s',
1636
      $fb->publicGetSharedSessionID(),
1637
      self::APP_ID,
1638
      $key
1639
    );
1640
    $fb->publicSetPersistentData($key, $val);
1641
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1642
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1643
  }
1644
1645
  public function testSharedSessionBackedFacebookIgnoresUnsupportedKey() {
1646
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1647
    $fb = new PersistentFBPublic(array(
1648
      'appId'  => self::APP_ID,
1649
      'secret' => self::SECRET,
1650
      'sharedSession' => true,
1651
    ));
1652
    $key = '--invalid--';
1653
    $val = 'foo';
1654
    $session_var_name = sprintf(
1655
      '%s_fb_%s_%s',
1656
      $fb->publicGetSharedSessionID(),
1657
      self::APP_ID,
1658
      $key
1659
    );
1660
    $fb->publicSetPersistentData($key, $val);
1661
    $this->assertFalse(array_key_exists($session_var_name, $_SESSION));
1662
    $this->assertFalse($fb->publicGetPersistentData($key));
1663
  }
1664
1665
  public function testSharedClearSessionBackedFacebook() {
1666
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1667
    $fb = new PersistentFBPublic(array(
1668
      'appId'  => self::APP_ID,
1669
      'secret' => self::SECRET,
1670
      'sharedSession' => true,
1671
    ));
1672
    $key = 'state';
1673
    $val = 'foo';
1674
    $session_var_name = sprintf(
1675
      '%s_fb_%s_%s',
1676
      $fb->publicGetSharedSessionID(),
1677
      self::APP_ID,
1678
      $key
1679
    );
1680
    $fb->publicSetPersistentData($key, $val);
1681
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1682
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1683
    $fb->publicClearPersistentData($key);
1684
    $this->assertFalse(array_key_exists($session_var_name, $_SESSION));
1685
    $this->assertFalse($fb->publicGetPersistentData($key));
1686
  }
1687
1688
  public function testSharedSessionBackedFacebookIgnoresUnsupportedKeyInClear() {
1689
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1690
    $fb = new PersistentFBPublic(array(
1691
      'appId'  => self::APP_ID,
1692
      'secret' => self::SECRET,
1693
      'sharedSession' => true,
1694
    ));
1695
    $key = '--invalid--';
1696
    $val = 'foo';
1697
    $session_var_name = sprintf(
1698
      '%s_fb_%s_%s',
1699
      $fb->publicGetSharedSessionID(),
1700
      self::APP_ID,
1701
      $key
1702
    );
1703
    $_SESSION[$session_var_name] = $val;
1704
    $fb->publicClearPersistentData($key);
1705
    $this->assertTrue(array_key_exists($session_var_name, $_SESSION));
1706
    $this->assertFalse($fb->publicGetPersistentData($key));
1707
  }
1708
1709
  public function testSharedClearAllSessionBackedFacebook() {
1710
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1711
    $fb = new PersistentFBPublic(array(
1712
      'appId'  => self::APP_ID,
1713
      'secret' => self::SECRET,
1714
      'sharedSession' => true,
1715
    ));
1716
    $key = 'state';
1717
    $val = 'foo';
1718
    $session_var_name = sprintf(
1719
      '%s_fb_%s_%s',
1720
      $fb->publicGetSharedSessionID(),
1721
      self::APP_ID,
1722
      $key
1723
    );
1724
    $fb->publicSetPersistentData($key, $val);
1725
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1726
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1727
    $fb->publicClearAllPersistentData();
1728
    $this->assertFalse(array_key_exists($session_var_name, $_SESSION));
1729
    $this->assertFalse($fb->publicGetPersistentData($key));
1730
  }
1731
1732
  public function testSharedSessionBackedFacebookIsRestored() {
1733
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1734
    $fb = new PersistentFBPublic(array(
1735
      'appId'  => self::APP_ID,
1736
      'secret' => self::SECRET,
1737
      'sharedSession' => true,
1738
    ));
1739
    $key = 'state';
1740
    $val = 'foo';
1741
    $shared_session_id = $fb->publicGetSharedSessionID();
1742
    $session_var_name = sprintf(
1743
      '%s_fb_%s_%s',
1744
      $shared_session_id,
1745
      self::APP_ID,
1746
      $key
1747
    );
1748
    $fb->publicSetPersistentData($key, $val);
1749
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1750
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1751
1752
    // check the new instance has the same data
1753
    $fb = new PersistentFBPublic(array(
1754
      'appId'  => self::APP_ID,
1755
      'secret' => self::SECRET,
1756
      'sharedSession' => true,
1757
    ));
1758
    $this->assertEquals(
1759
      $shared_session_id,
1760
      $fb->publicGetSharedSessionID()
1761
    );
1762
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1763
  }
1764
1765
  public function testSharedSessionBackedFacebookIsNotRestoredWhenCorrupt() {
1766
    $_SERVER['HTTP_HOST'] = 'fbrell.com';
1767
    $fb = new PersistentFBPublic(array(
1768
      'appId'  => self::APP_ID,
1769
      'secret' => self::SECRET,
1770
      'sharedSession' => true,
1771
    ));
1772
    $key = 'state';
1773
    $val = 'foo';
1774
    $shared_session_id = $fb->publicGetSharedSessionID();
1775
    $session_var_name = sprintf(
1776
      '%s_fb_%s_%s',
1777
      $shared_session_id,
1778
      self::APP_ID,
1779
      $key
1780
    );
1781
    $fb->publicSetPersistentData($key, $val);
1782
    $this->assertEquals($val, $_SESSION[$session_var_name]);
1783
    $this->assertEquals($val, $fb->publicGetPersistentData($key));
1784
1785
    // break the cookie
1786
    $cookie_name = $fb->publicGetSharedSessionCookieName();
1787
    $_COOKIE[$cookie_name] = substr($_COOKIE[$cookie_name], 1);
1788
1789
    // check the new instance does not have the data
1790
    $fb = new PersistentFBPublic(array(
1791
      'appId'  => self::APP_ID,
1792
      'secret' => self::SECRET,
1793
      'sharedSession' => true,
1794
    ));
1795
    $this->assertFalse($fb->publicGetPersistentData($key));
1796
    $this->assertNotEquals(
1797
      $shared_session_id,
1798
      $fb->publicGetSharedSessionID()
1799
    );
1800
  }
1801
1802
  public function testHttpHost() {
1803
    $real = 'foo.com';
1804
    $_SERVER['HTTP_HOST'] = $real;
1805
    $_SERVER['HTTP_X_FORWARDED_HOST'] = 'evil.com';
1806
    $fb = new PersistentFBPublic(array(
1807
      'appId'  => self::APP_ID,
1808
      'secret' => self::SECRET,
1809
    ));
1810
    $this->assertEquals($real, $fb->publicGetHttpHost());
1811
  }
1812
1813
  public function testHttpProtocol() {
1814
    $_SERVER['HTTPS'] = 'on';
1815
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
1816
    $fb = new PersistentFBPublic(array(
1817
      'appId'  => self::APP_ID,
1818
      'secret' => self::SECRET,
1819
    ));
1820
    $this->assertEquals('https', $fb->publicGetHttpProtocol());
1821
  }
1822
1823
  public function testHttpHostForwarded() {
1824
    $real = 'foo.com';
1825
    $_SERVER['HTTP_HOST'] = 'localhost';
1826
    $_SERVER['HTTP_X_FORWARDED_HOST'] = $real;
1827
    $fb = new PersistentFBPublic(array(
1828
      'appId'  => self::APP_ID,
1829
      'secret' => self::SECRET,
1830
      'trustForwarded' => true,
1831
    ));
1832
    $this->assertEquals($real, $fb->publicGetHttpHost());
1833
  }
1834
1835
  public function testHttpProtocolForwarded() {
1836
    $_SERVER['HTTPS'] = 'on';
1837
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
1838
    $fb = new PersistentFBPublic(array(
1839
      'appId'  => self::APP_ID,
1840
      'secret' => self::SECRET,
1841
      'trustForwarded' => true,
1842
    ));
1843
    $this->assertEquals('http', $fb->publicGetHttpProtocol());
1844
  }
1845
1846
  public function testHttpProtocolForwardedSecure() {
1847
    $_SERVER['HTTPS'] = 'on';
1848
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
1849
    $fb = new PersistentFBPublic(array(
1850
      'appId'  => self::APP_ID,
1851
      'secret' => self::SECRET,
1852
      'trustForwarded' => true,
1853
    ));
1854
    $this->assertEquals('https', $fb->publicGetHttpProtocol());
1855
  }
1856
1857
  /**
1858
   * @dataProvider provideEndsWith
1859
   */
1860
  public function testEndsWith($big, $small, $result) {
1861
    $this->assertEquals(
1862
      $result,
1863
      PersistentFBPublic::publicEndsWith($big, $small)
1864
    );
1865
  }
1866
1867
  public function provideEndsWith() {
1868
    return array(
1869
      array('', '', true),
1870
      array('', 'a', false),
1871
      array('a', '', true),
1872
      array('a', 'b', false),
1873
      array('a', 'a', true),
1874
      array('aa', 'a', true),
1875
      array('ab', 'a', false),
1876
      array('ba', 'a', true),
1877
    );
1878
  }
1879
1880
  /**
1881
   * @dataProvider provideIsAllowedDomain
1882
   */
1883
  public function testIsAllowedDomain($big, $small, $result) {
1884
    $this->assertEquals(
1885
      $result,
1886
      PersistentFBPublic::publicIsAllowedDomain($big, $small)
1887
    );
1888
  }
1889
1890
  public function provideIsAllowedDomain() {
1891
    return array(
1892
      array('fbrell.com', 'fbrell.com', true),
1893
      array('foo.fbrell.com', 'fbrell.com', true),
1894
      array('foofbrell.com', 'fbrell.com', false),
1895
      array('evil.com', 'fbrell.com', false),
1896
      array('foo.fbrell.com', 'bar.fbrell.com', false),
1897
    );
1898
  }
1899
1900
  protected function generateMD5HashOfRandomValue() {
1901
    return md5(uniqid(mt_rand(), true));
1902
  }
1903
1904
  protected function setUp() {
1905
    parent::setUp();
1906
  }
1907
1908
  protected function tearDown() {
1909
    $this->clearSuperGlobals();
1910
    parent::tearDown();
1911
  }
1912
1913
  protected function clearSuperGlobals() {
1914
    unset($_SERVER['HTTPS']);
1915
    unset($_SERVER['HTTP_HOST']);
1916
    unset($_SERVER['REQUEST_URI']);
1917
    $_SESSION = array();
1918
    $_COOKIE = array();
1919
    $_REQUEST = array();
1920
    $_POST = array();
1921
    $_GET = array();
1922
    if (session_id()) {
1923
      session_destroy();
1924
    }
1925
  }
1926
1927
  /**
1928
   * Checks that the correct args are a subset of the returned obj
1929
   * @param  array $correct The correct array values
1930
   * @param  array $actual  The values in practice
1931
   * @param  string $message to be shown on failure
0 ignored issues
show
Bug introduced by
There is no parameter named $message. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1932
   */
1933
  protected function assertIsSubset($correct, $actual, $msg='') {
1934
    foreach ($correct as $key => $value) {
1935
      $actual_value = $actual[$key];
1936
      $newMsg = (strlen($msg) ? ($msg.' ') : '').'Key: '.$key;
1937
      $this->assertEquals($value, $actual_value, $newMsg);
1938
    }
1939
  }
1940
}
1941
1942
class TransientFacebook extends BaseFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1943
  protected function setPersistentData($key, $value) {}
1944
  protected function getPersistentData($key, $default = false) {
1945
    return $default;
1946
  }
1947
  protected function clearPersistentData($key) {}
1948
  protected function clearAllPersistentData() {}
1949
}
1950
1951
class FBRecordURL extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1952
  private $url;
1953
1954
  protected function _oauthRequest($url, $params) {
1955
    $this->url = $url;
1956
  }
1957
1958
  public function getRequestedURL() {
1959
    return $this->url;
1960
  }
1961
}
1962
1963
class FBRecordMakeRequest extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1964
  private $requests = array();
1965
1966
  protected function makeRequest($url, $params, $ch=null) {
1967
    $this->requests[] = array(
1968
      'url' => $url,
1969
      'params' => $params,
1970
    );
1971
    return parent::makeRequest($url, $params, $ch);
1972
  }
1973
1974
  public function publicGetRequests() {
1975
    return $this->requests;
1976
  }
1977
1978
  public function publicGetAppSecretProof($access_token) {
1979
    return $this->getAppSecretProof($access_token);
1980
  }
1981
}
1982
1983
class FBPublic extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1984
  public static function publicBase64UrlDecode($input) {
1985
    return self::base64UrlDecode($input);
1986
  }
1987
  public static function publicBase64UrlEncode($input) {
1988
    return self::base64UrlEncode($input);
1989
  }
1990
  public function publicParseSignedRequest($input) {
1991
    return $this->parseSignedRequest($input);
1992
  }
1993
  public function publicMakeSignedRequest($data) {
1994
    return $this->makeSignedRequest($data);
1995
  }
1996
}
1997
1998
class PersistentFBPublic extends Facebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1999
  public function publicParseSignedRequest($input) {
2000
    return $this->parseSignedRequest($input);
2001
  }
2002
2003
  public function publicSetPersistentData($key, $value) {
2004
    $this->setPersistentData($key, $value);
2005
  }
2006
2007
  public function publicGetPersistentData($key, $default = false) {
2008
    return $this->getPersistentData($key, $default);
2009
  }
2010
2011
  public function publicClearPersistentData($key) {
2012
    return $this->clearPersistentData($key);
2013
  }
2014
2015
  public function publicClearAllPersistentData() {
2016
    return $this->clearAllPersistentData();
2017
  }
2018
2019
  public function publicGetSharedSessionID() {
2020
    return $this->sharedSessionID;
2021
  }
2022
2023
  public static function publicIsAllowedDomain($big, $small) {
2024
    return self::isAllowedDomain($big, $small);
2025
  }
2026
2027
  public static function publicEndsWith($big, $small) {
2028
    return self::endsWith($big, $small);
2029
  }
2030
2031
  public function publicGetSharedSessionCookieName() {
2032
    return $this->getSharedSessionCookieName();
2033
  }
2034
2035
  public function publicGetHttpHost() {
2036
    return $this->getHttpHost();
2037
  }
2038
2039
  public function publicGetHttpProtocol() {
2040
    return $this->getHttpProtocol();
2041
  }
2042
}
2043
2044
class FBCode extends Facebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2045
  public function publicGetCode() {
2046
    return $this->getCode();
2047
  }
2048
2049
  public function publicGetState() {
2050
    return $this->state;
2051
  }
2052
2053
  public function setCSRFStateToken() {
2054
    $this->establishCSRFTokenState();
2055
  }
2056
2057
  public function getCSRFStateToken() {
2058
    return $this->getPersistentData('state');
2059
  }
2060
}
2061
2062
class FBAccessToken extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2063
  public function publicGetApplicationAccessToken() {
2064
    return $this->getApplicationAccessToken();
2065
  }
2066
}
2067
2068
class FBGetCurrentURLFacebook extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2069
  public function publicGetCurrentUrl() {
2070
    return $this->getCurrentUrl();
2071
  }
2072
}
2073
2074
class FBPublicCookie extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2075
  public function publicGetSignedRequest() {
2076
    return $this->getSignedRequest();
2077
  }
2078
2079
  public function publicGetSignedRequestCookieName() {
2080
    return $this->getSignedRequestCookieName();
2081
  }
2082
2083
  public function publicGetMetadataCookie() {
2084
    return $this->getMetadataCookie();
2085
  }
2086
2087
  public function publicGetMetadataCookieName() {
2088
    return $this->getMetadataCookieName();
2089
  }
2090
}
2091
2092
class FBRewrite extends Facebook{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2093
2094
  public function uncacheSignedRequest(){
2095
    $this->signedRequest = null;
2096
  }
2097
2098
  public function uncache()
2099
  {
2100
    $this->user = null;
2101
    $this->signedRequest = null;
2102
    $this->accessToken = null;
2103
  }
2104
}
2105
2106
2107
class FBPublicGetAccessTokenFromCode extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2108
  public function publicGetAccessTokenFromCode($code, $redirect_uri = null) {
2109
    return $this->getAccessTokenFromCode($code, $redirect_uri);
2110
  }
2111
}
2112
2113
class FBPublicState extends TransientFacebook {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
2114
  const STATE = 'foo';
2115
  protected function getPersistentData($key, $default = false) {
2116
    if ($key === 'state') {
2117
      return self::STATE;
2118
    }
2119
    return parent::getPersistentData($key, $default);
2120
  }
2121
2122
  public function publicGetState() {
2123
    return $this->state;
2124
  }
2125
}
2126