Project

General

Profile

Paste
Download (36.6 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / openid / openid.test @ b0dc3a2e

1
<?php
2

    
3
/**
4
 * @file
5
 * Tests for openid.module.
6
 */
7

    
8
/**
9
 * Base class for OpenID tests.
10
 */
11
abstract class OpenIDWebTestCase extends DrupalWebTestCase {
12

    
13
  /**
14
   * Initiates the login procedure using the specified User-supplied Identity.
15
   */
16
  function submitLoginForm($identity) {
17
    // Fill out and submit the login form.
18
    $edit = array('openid_identifier' => $identity);
19
    $this->drupalPost('', $edit, t('Log in'));
20

    
21
    // Check we are on the OpenID redirect form.
22
    $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
23

    
24
    // Submit form to the OpenID Provider Endpoint.
25
    $this->drupalPost(NULL, array(), t('Send'));
26
  }
27

    
28
  /**
29
   * Parses the last sent e-mail and returns the one-time login link URL.
30
   */
31
  function getPasswordResetURLFromMail() {
32
    $mails = $this->drupalGetMails();
33
    $mail = end($mails);
34
    preg_match('@.+user/reset/.+@', $mail['body'], $matches);
35
    return $matches[0];
36
  }
37
}
38

    
39
/**
40
 * Test discovery and login using OpenID
41
 */
42
class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
43
  protected $web_user;
44

    
45
  public static function getInfo() {
46
    return array(
47
      'name' => 'OpenID discovery and login',
48
      'description' => "Adds an identity to a user's profile and uses it to log in.",
49
      'group' => 'OpenID'
50
    );
51
  }
52

    
53
  function setUp() {
54
    parent::setUp('openid', 'openid_test');
55

    
56
    // User doesn't need special permissions; only the ability to log in.
57
    $this->web_user = $this->drupalCreateUser(array());
58
  }
59

    
60
  /**
61
   * Test discovery of OpenID Provider Endpoint via Yadis and HTML.
62
   */
63
  function testDiscovery() {
64
    $this->drupalLogin($this->web_user);
65

    
66
    // The User-supplied Identifier entered by the user may indicate the URL of
67
    // the OpenID Provider Endpoint in various ways, as described in OpenID
68
    // Authentication 2.0 and Yadis Specification 1.0.
69
    // Note that all of the tested identifiers refer to the same endpoint, so
70
    // only the first will trigger an associate request in openid_association()
71
    // (association is only done the first time Drupal encounters a given
72
    // endpoint).
73

    
74

    
75
    // Yadis discovery (see Yadis Specification 1.0, section 6.2.5):
76
    // If the User-supplied Identifier is a URL, it may be a direct or indirect
77
    // reference to an XRDS document (a Yadis Resource Descriptor) that contains
78
    // the URL of the OpenID Provider Endpoint.
79

    
80
    // Identifier is the URL of an XRDS document.
81
    // On HTTP test environments, the URL scheme is stripped in order to test
82
    // that the supplied identifier is normalized in openid_begin().
83
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
84
    $this->addIdentity(preg_replace('@^http://@', '', $identity), 2, 'http://example.com/xrds', $identity);
85

    
86
    $identity = url('openid-test/yadis/xrds/delegate', array('absolute' => TRUE));
87
    $this->addIdentity(preg_replace('@^http://@', '', $identity), 2, 'http://example.com/xrds-delegate', $identity);
88

    
89
    // Identifier is the URL of an XRDS document containing an OP Identifier
90
    // Element. The Relying Party sends the special value
91
    // "http://specs.openid.net/auth/2.0/identifier_select" as Claimed
92
    // Identifier. The OpenID Provider responds with the actual identifier
93
    // including the fragment.
94
    $identity = url('openid-test/yadis/xrds/dummy-user', array('absolute' => TRUE, 'fragment' => $this->randomName()));
95
    // Tell openid_test.module to respond with this identifier. If the fragment
96
    // part is present in the identifier, it should be retained.
97
    variable_set('openid_test_response', array('openid.claimed_id' => $identity, 'openid.identity' => openid_normalize($identity)));
98
    $this->addIdentity(url('openid-test/yadis/xrds/server', array('absolute' => TRUE)), 2, 'http://specs.openid.net/auth/2.0/identifier_select', $identity);
99
    variable_set('openid_test_response', array());
100

    
101
    // Identifier is the URL of an HTML page that is sent with an HTTP header
102
    // that contains the URL of an XRDS document.
103
    $this->addIdentity(url('openid-test/yadis/x-xrds-location', array('absolute' => TRUE)), 2);
104

    
105
    // Identifier is the URL of an HTML page containing a <meta http-equiv=...>
106
    // element that contains the URL of an XRDS document.
107
    $this->addIdentity(url('openid-test/yadis/http-equiv', array('absolute' => TRUE)), 2);
108

    
109
    // Identifier is an XRI. Resolve using our own dummy proxy resolver.
110
    variable_set('xri_proxy_resolver', url('openid-test/yadis/xrds/xri', array('absolute' => TRUE)) . '/');
111
    $this->addIdentity('@example*résumé;%25', 2, 'http://example.com/xrds', 'http://example.com/user');
112

    
113
    // Make sure that unverified CanonicalID are not trusted.
114
    variable_set('openid_test_canonical_id_status', 'bad value');
115
    $this->addIdentity('@example*résumé;%25', 2, FALSE, FALSE);
116

    
117
    // HTML-based discovery:
118
    // If the User-supplied Identifier is a URL of an HTML page, the page may
119
    // contain a <link rel=...> element containing the URL of the OpenID
120
    // Provider Endpoint. OpenID 1 and 2 describe slightly different formats.
121

    
122
    // OpenID Authentication 1.1, section 3.1:
123
    $this->addIdentity(url('openid-test/html/openid1', array('absolute' => TRUE)), 1, 'http://example.com/html-openid1');
124

    
125
    // OpenID Authentication 2.0, section 7.3.3:
126
    $this->addIdentity(url('openid-test/html/openid2', array('absolute' => TRUE)), 2, 'http://example.com/html-openid2');
127

    
128
    // OpenID Authentication 2.0, section 7.2.4:
129
    // URL Identifiers MUST then be further normalized by both (1) following
130
    // redirects when retrieving their content and finally (2) applying the
131
    // rules in Section 6 of RFC3986 to the final destination URL. This final
132
    // URL MUST be noted by the Relying Party as the Claimed Identifier and be
133
    // used when requesting authentication.
134

    
135
    // Single redirect.
136
    $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/1', array('absolute' => TRUE));
137
    $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 0);
138

    
139
    // Exact 3 redirects (default value for the 'max_redirects' option in
140
    // drupal_http_request()).
141
    $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/2', array('absolute' => TRUE));
142
    $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 2);
143

    
144
    // Fails because there are more than 3 redirects (default value for the
145
    // 'max_redirects' option in drupal_http_request()).
146
    $identity = url('openid-test/redirected/yadis/xrds/3', array('absolute' => TRUE));
147
    $expected_claimed_id = FALSE;
148
    $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 3);
149
  }
150

    
151
  /**
152
   * Test login using OpenID.
153
   */
154
  function testLogin() {
155
    $this->drupalLogin($this->web_user);
156

    
157
    // Use a User-supplied Identity that is the URL of an XRDS document.
158
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
159
    $this->addIdentity($identity);
160

    
161
    $this->drupalLogout();
162

    
163
    // Test logging in via the login block on the front page.
164
    $this->submitLoginForm($identity);
165
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
166

    
167
    $this->drupalLogout();
168

    
169
    // Test logging in via the user/login page.
170
    $edit = array('openid_identifier' => $identity);
171
    $this->drupalPost('user/login', $edit, t('Log in'));
172

    
173
    // Check we are on the OpenID redirect form.
174
    $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
175

    
176
    // Submit form to the OpenID Provider Endpoint.
177
    $this->drupalPost(NULL, array(), t('Send'));
178

    
179
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
180

    
181
    // Verify user was redirected away from user/login to an accessible page.
182
    $this->assertResponse(200);
183

    
184
    $this->drupalLogout();
185
    // Use a User-supplied Identity that is the URL of an XRDS document.
186
    // Tell the test module to add a doctype. This should fail.
187
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'query' => array('doctype' => 1)));
188
    // Test logging in via the login block on the front page.
189
    $edit = array('openid_identifier' => $identity);
190
    $this->drupalPost('', $edit, t('Log in'));
191
    $this->assertRaw(t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'), 'XML with DOCTYPE was rejected.');
192
  }
193

    
194
  /**
195
   * Test login using OpenID during maintenance mode.
196
   */
197
  function testLoginMaintenanceMode() {
198
    $this->web_user = $this->drupalCreateUser(array('access site in maintenance mode'));
199
    $this->drupalLogin($this->web_user);
200

    
201
    // Use a User-supplied Identity that is the URL of an XRDS document.
202
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
203
    $this->addIdentity($identity);
204
    $this->drupalLogout();
205

    
206
    // Enable maintenance mode.
207
    variable_set('maintenance_mode', 1);
208

    
209
    // Test logging in via the user/login page while the site is offline.
210
    $edit = array('openid_identifier' => $identity);
211
    $this->drupalPost('user/login', $edit, t('Log in'));
212

    
213
    // Check we are on the OpenID redirect form.
214
    $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
215

    
216
    // Submit form to the OpenID Provider Endpoint.
217
    $this->drupalPost(NULL, array(), t('Send'));
218

    
219
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
220

    
221
    // Verify user was redirected away from user/login to an accessible page.
222
    $this->assertText(t('Operating in maintenance mode.'));
223
    $this->assertResponse(200);
224
  }
225

    
226
  /**
227
   * Test deleting an OpenID identity from a user's profile.
228
   */
229
  function testDelete() {
230
    $this->drupalLogin($this->web_user);
231

    
232
    // Add identity to user's profile.
233
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
234
    $this->addIdentity($identity);
235
    $this->assertText($identity, 'Identity appears in list.');
236

    
237
    // Delete the newly added identity.
238
    $this->clickLink(t('Delete'));
239
    $this->drupalPost(NULL, array(), t('Confirm'));
240

    
241
    $this->assertText(t('OpenID deleted.'), 'Identity deleted');
242
    $this->assertNoText($identity, 'Identity no longer appears in list.');
243
  }
244

    
245
  /**
246
   * Test that a blocked user cannot log in.
247
   */
248
  function testBlockedUserLogin() {
249
    // Use a User-supplied Identity that is the URL of an XRDS document.
250
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
251

    
252
    // Log in and add an OpenID Identity to the account.
253
    $this->drupalLogin($this->web_user);
254
    $this->addIdentity($identity);
255
    $this->drupalLogout();
256

    
257
    // Log in as an admin user and block the account.
258
    $admin_user = $this->drupalCreateUser(array('administer users'));
259
    $this->drupalLogin($admin_user);
260
    $this->drupalGet('admin/people');
261
    $edit = array(
262
      'operation' => 'block',
263
      'accounts[' . $this->web_user->uid . ']' => TRUE,
264
    );
265
    $this->drupalPost('admin/people', $edit, t('Update'));
266
    $this->assertRaw('The update has been performed.', 'Account was blocked.');
267
    $this->drupalLogout();
268

    
269
    $this->submitLoginForm($identity);
270
    $this->assertRaw(t('The username %name has not been activated or is blocked.', array('%name' => $this->web_user->name)), 'User login was blocked.');
271
  }
272

    
273
  /**
274
   * Add OpenID identity to user's profile.
275
   *
276
   * @param $identity
277
   *   The User-supplied Identifier.
278
   * @param $version
279
   *   The protocol version used by the service.
280
   * @param $local_id
281
   *   The expected OP-Local Identifier found during discovery.
282
   * @param $claimed_id
283
   *   The expected Claimed Identifier returned by the OpenID Provider, or FALSE
284
   *   if the discovery is expected to fail.
285
   */
286
  function addIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL) {
287
    // Tell openid_test.module to only accept this OP-Local Identifier.
288
    variable_set('openid_test_identity', $local_id);
289

    
290
    $edit = array('openid_identifier' => $identity);
291
    $this->drupalPost('user/' . $this->web_user->uid . '/openid', $edit, t('Add an OpenID'));
292

    
293
    if ($claimed_id === FALSE) {
294
      $this->assertRaw(t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'), 'Invalid identity was rejected.');
295
      return;
296
    }
297

    
298
    // OpenID 1 used a HTTP redirect, OpenID 2 uses a HTML form that is submitted automatically using JavaScript.
299
    if ($version == 2) {
300
      // Check we are on the OpenID redirect form.
301
      $this->assertTitle(t('OpenID redirect'), 'OpenID redirect page was displayed.');
302

    
303
      // Submit form to the OpenID Provider Endpoint.
304
      $this->drupalPost(NULL, array(), t('Send'));
305
    }
306

    
307
    if (!isset($claimed_id)) {
308
      $claimed_id = $identity;
309
    }
310
    $this->assertRaw(t('Successfully added %identity', array('%identity' => $claimed_id)), format_string('Identity %identity was added.', array('%identity' => $identity)));
311
  }
312

    
313
  /**
314
   * Add OpenID identity, changed by the following redirects, to user's profile.
315
   *
316
   * According to OpenID Authentication 2.0, section 7.2.4, URL Identifiers MUST
317
   * be further normalized by following redirects when retrieving their content
318
   * and this final URL MUST be noted by the Relying Party as the Claimed
319
   * Identifier and be used when requesting authentication.
320
   *
321
   * @param $identity
322
   *   The User-supplied Identifier.
323
   * @param $version
324
   *   The protocol version used by the service.
325
   * @param $local_id
326
   *   The expected OP-Local Identifier found during discovery.
327
   * @param $claimed_id
328
   *   The expected Claimed Identifier returned by the OpenID Provider, or FALSE
329
   *   if the discovery is expected to fail.
330
   * @param $redirects
331
   *   The number of redirects.
332
   */
333
  function addRedirectedIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL, $redirects = 0) {
334
    // Set the final destination URL which is the same as the Claimed
335
    // Identifier, we insert the same identifier also to the provider response,
336
    // but provider could further change the Claimed ID actually (e.g. it could
337
    // add unique fragment).
338
    variable_set('openid_test_redirect_url', $identity);
339
    variable_set('openid_test_response', array('openid.claimed_id' => $identity));
340

    
341
    $this->addIdentity(url('openid-test/redirect/' . $redirects, array('absolute' => TRUE)), $version, $local_id, $claimed_id);
342

    
343
    // Clean up.
344
    variable_del('openid_test_redirect_url');
345
    variable_del('openid_test_response');
346
  }
347

    
348
  /**
349
   * Tests that openid.signed is verified.
350
   */
351
  function testSignatureValidation() {
352
    // Use a User-supplied Identity that is the URL of an XRDS document.
353
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
354

    
355
    // Respond with an invalid signature.
356
    variable_set('openid_test_response', array('openid.sig' => 'this-is-an-invalid-signature'));
357
    $this->submitLoginForm($identity);
358
    $this->assertRaw('OpenID login failed.');
359

    
360
    // Do not sign the mandatory field openid.assoc_handle.
361
    variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce'));
362
    $this->submitLoginForm($identity);
363
    $this->assertRaw('OpenID login failed.');
364

    
365
    // Sign all mandatory fields and a custom field.
366
    $keys_to_sign = array('op_endpoint', 'claimed_id', 'identity', 'return_to', 'response_nonce', 'assoc_handle', 'foo');
367
    $association = new stdClass();
368
    $association->mac_key = variable_get('mac_key');
369
    $response = array(
370
      'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)),
371
      'openid.claimed_id' => $identity,
372
      'openid.identity' => $identity,
373
      'openid.return_to' => url('openid/authenticate', array('absolute' => TRUE)),
374
      'openid.response_nonce' => _openid_nonce(),
375
      'openid.assoc_handle' => 'openid-test',
376
      'openid.foo' => 123,
377
      'openid.signed' => implode(',', $keys_to_sign),
378
    );
379
    $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
380
    variable_set('openid_test_response', $response);
381
    $this->submitLoginForm($identity);
382
    $this->assertNoRaw('OpenID login failed.');
383
    $this->assertFieldByName('name', '', 'No username was supplied by provider.');
384
    $this->assertFieldByName('mail', '', 'No e-mail address was supplied by provider.');
385

    
386
    // Check that unsigned SREG fields are ignored.
387
    $response = array(
388
      'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,sreg.nickname',
389
      'openid.sreg.nickname' => 'john',
390
      'openid.sreg.email' => 'john@example.com',
391
    );
392
    variable_set('openid_test_response', $response);
393
    $this->submitLoginForm($identity);
394
    $this->assertNoRaw('OpenID login failed.');
395
    $this->assertFieldByName('name', 'john', 'Username was supplied by provider.');
396
    $this->assertFieldByName('mail', '', 'E-mail address supplied by provider was ignored.');
397
  }
398
}
399

    
400
/**
401
 * Test account registration using Simple Registration and Attribute Exchange.
402
 */
403
class OpenIDRegistrationTestCase extends OpenIDWebTestCase {
404
  public static function getInfo() {
405
    return array(
406
      'name' => 'OpenID account registration',
407
      'description' => 'Creates a user account using auto-registration.',
408
      'group' => 'OpenID'
409
    );
410
  }
411

    
412
  function setUp() {
413
    parent::setUp('openid', 'openid_test');
414
    variable_set('user_register', USER_REGISTER_VISITORS);
415
  }
416

    
417
  /**
418
   * Test OpenID auto-registration with e-mail verification enabled.
419
   */
420
  function testRegisterUserWithEmailVerification() {
421
    variable_set('user_email_verification', TRUE);
422

    
423
    // Tell openid_test.module to respond with these SREG fields.
424
    variable_set('openid_test_response', array('openid.sreg.nickname' => 'john', 'openid.sreg.email' => 'john@example.com'));
425

    
426
    // Use a User-supplied Identity that is the URL of an XRDS document.
427
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
428
    $this->submitLoginForm($identity);
429
    $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
430
    $this->assertRaw(t('A welcome message with further instructions has been sent to your e-mail address.'), 'A welcome message was sent to the user.');
431
    $reset_url = $this->getPasswordResetURLFromMail();
432

    
433
    $user = user_load_by_name('john');
434
    $this->assertTrue($user, 'User was registered with right username.');
435
    $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
436
    $this->assertFalse($user->data, 'No additional user info was saved.');
437

    
438
    $this->submitLoginForm($identity);
439
    $this->assertRaw(t('You must validate your email address for this account before logging in via OpenID.'));
440

    
441
    // Follow the one-time login that was sent in the welcome e-mail.
442
    $this->drupalGet($reset_url);
443
    $this->drupalPost(NULL, array(), t('Log in'));
444

    
445
    $this->drupalLogout();
446

    
447
    // Verify that the account was activated.
448
    $this->submitLoginForm($identity);
449
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
450
  }
451

    
452
  /**
453
   * Test OpenID auto-registration with e-mail verification disabled.
454
   */
455
  function testRegisterUserWithoutEmailVerification() {
456
    variable_set('user_email_verification', FALSE);
457

    
458
    // Tell openid_test.module to respond with these SREG fields.
459
    variable_set('openid_test_response', array('openid.sreg.nickname' => 'john', 'openid.sreg.email' => 'john@example.com'));
460

    
461
    // Use a User-supplied Identity that is the URL of an XRDS document.
462
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
463
    $this->submitLoginForm($identity);
464
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
465

    
466
    $user = user_load_by_name('john');
467
    $this->assertTrue($user, 'User was registered with right username.');
468
    $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
469
    $this->assertFalse($user->data, 'No additional user info was saved.');
470

    
471
    $this->drupalLogout();
472

    
473
    $this->submitLoginForm($identity);
474
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
475
  }
476

    
477
  /**
478
   * Test OpenID auto-registration with a provider that supplies invalid SREG
479
   * information (a username that is already taken, and no e-mail address).
480
   */
481
  function testRegisterUserWithInvalidSreg() {
482
    // Tell openid_test.module to respond with these SREG fields.
483
    $web_user = $this->drupalCreateUser(array());
484
    variable_set('openid_test_response', array('openid.sreg.nickname' => $web_user->name, 'openid.sreg.email' => 'mail@invalid#'));
485

    
486
    // Use a User-supplied Identity that is the URL of an XRDS document.
487
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
488
    $this->submitLoginForm($identity);
489

    
490
    $this->assertRaw(t('Account registration using the information provided by your OpenID provider failed due to the reasons listed below. Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'User was asked to complete the registration process manually.');
491
    $this->assertRaw(t('The name %name is already taken.', array('%name' => $web_user->name)), 'Form validation error for username was displayed.');
492
    $this->assertRaw(t('The e-mail address %mail is not valid.', array('%mail' => 'mail@invalid#')), 'Form validation error for e-mail address was displayed.');
493

    
494
    // Enter username and e-mail address manually.
495
    $edit = array('name' => 'john', 'mail' => 'john@example.com');
496
    $this->drupalPost(NULL, $edit, t('Create new account'));
497
    $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
498
    $reset_url = $this->getPasswordResetURLFromMail();
499

    
500
    $user = user_load_by_name('john');
501
    $this->assertTrue($user, 'User was registered with right username.');
502
    $this->assertFalse($user->data, 'No additional user info was saved.');
503

    
504
    // Follow the one-time login that was sent in the welcome e-mail.
505
    $this->drupalGet($reset_url);
506
    $this->drupalPost(NULL, array(), t('Log in'));
507

    
508
    // The user is taken to user/%uid/edit.
509
    $this->assertFieldByName('mail', 'john@example.com', 'User was registered with right e-mail address.');
510

    
511
    $this->clickLink(t('OpenID identities'));
512
    $this->assertRaw($identity, 'OpenID identity was registered.');
513
  }
514

    
515
  /**
516
   * Test OpenID auto-registration with a provider that does not supply SREG
517
   * information (i.e. no username or e-mail address).
518
   */
519
  function testRegisterUserWithoutSreg() {
520
    // Load the front page to get the user login block.
521
    $this->drupalGet('');
522

    
523
    // Use a User-supplied Identity that is the URL of an XRDS document.
524
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
525
    $this->submitLoginForm($identity);
526
    $this->assertRaw(t('Complete the registration by filling out the form below. If you already have an account, you can <a href="@login">log in</a> now and add your OpenID under "My account".', array('@login' => url('user/login'))), 'User was asked to complete the registration process manually.');
527
    $this->assertNoRaw(t('You must enter a username.'), 'Form validation error for username was not displayed.');
528
    $this->assertNoRaw(t('You must enter an e-mail address.'), 'Form validation error for e-mail address was not displayed.');
529

    
530
    // Enter username and e-mail address manually.
531
    $edit = array('name' => 'john', 'mail' => 'john@example.com');
532
    $this->drupalPost(NULL, $edit, t('Create new account'));
533
    $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), 'User was asked to verify e-mail address.');
534
    $reset_url = $this->getPasswordResetURLFromMail();
535

    
536
    $user = user_load_by_name('john');
537
    $this->assertTrue($user, 'User was registered with right username.');
538
    $this->assertFalse($user->data, 'No additional user info was saved.');
539

    
540
    // Follow the one-time login that was sent in the welcome e-mail.
541
    $this->drupalGet($reset_url);
542
    $this->drupalPost(NULL, array(), t('Log in'));
543

    
544
    // The user is taken to user/%uid/edit.
545
    $this->assertFieldByName('mail', 'john@example.com', 'User was registered with right e-mail address.');
546

    
547
    $this->clickLink(t('OpenID identities'));
548
    $this->assertRaw($identity, 'OpenID identity was registered.');
549
  }
550

    
551
  /**
552
   * Test OpenID auto-registration with a provider that supplies AX information,
553
   * but no SREG.
554
   */
555
  function testRegisterUserWithAXButNoSREG() {
556
    variable_set('user_email_verification', FALSE);
557

    
558
    // Tell openid_test.module to respond with these AX fields.
559
    variable_set('openid_test_response', array(
560
      'openid.ns.ext123' => 'http://openid.net/srv/ax/1.0',
561
      'openid.ext123.type.mail456' => 'http://axschema.org/contact/email',
562
      'openid.ext123.value.mail456' => 'john@example.com',
563
      'openid.ext123.type.name789' => 'http://schema.openid.net/namePerson/friendly',
564
      'openid.ext123.count.name789' => '1',
565
      'openid.ext123.value.name789.1' => 'john',
566
    ));
567

    
568
    // Use a User-supplied Identity that is the URL of an XRDS document.
569
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
570
    $this->submitLoginForm($identity);
571
    $this->assertLink(t('Log out'), 0, 'User was logged in.');
572

    
573
    $user = user_load_by_name('john');
574
    $this->assertTrue($user, 'User was registered with right username.');
575
    $this->assertEqual($user->mail, 'john@example.com', 'User was registered with right email address.');
576
  }
577
}
578

    
579
/**
580
 * Test account registration using Simple Registration and Attribute Exchange.
581
 */
582
class OpenIDInvalidIdentifierTransitionTestCase extends OpenIDFunctionalTestCase  {
583

    
584
  public static function getInfo() {
585
    return array(
586
      'name' => 'OpenID account update',
587
      'description' => 'Tries to correct OpenID identifiers attached to accounts if their identifiers were stripped.',
588
      'group' => 'OpenID',
589
    );
590
  }
591

    
592
  function setUp() {
593
    parent::setUp('openid', 'openid_test');
594
    variable_set('user_register', USER_REGISTER_VISITORS);
595
    variable_set('openid_less_obtrusive_transition', TRUE);
596
  }
597

    
598
  /**
599
   * Test OpenID transition with e-mail mismatch.
600
   */
601
  function testStrippedFragmentAccountEmailMismatch() {
602
    $this->drupalLogin($this->web_user);
603

    
604
    // Use a User-supplied Identity that is the URL of an XRDS document.
605
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'fragment' => $this->randomName()));
606
    $identity_stripped = preg_replace('/#.*/', '', $identity);
607

    
608
    // Add invalid identifier to the authmap (identifier has stripped fragment).
609
    $this->addIdentity($identity_stripped);
610
    $this->drupalLogout();
611

    
612
    // Test logging in via the login form, provider will respond with full
613
    // identifier (including fragment) but with different email, so we can't
614
    // provide auto-update.
615
    variable_set('openid_test_response', array(
616
      'openid.claimed_id' => $identity,
617
      'openid.sreg.nickname' => $this->web_user->name,
618
      'openid.sreg.email' => 'invalid-' . $this->web_user->mail));
619

    
620
    $edit = array('openid_identifier' => $identity_stripped);
621
    $this->submitLoginForm($identity_stripped);
622

    
623
    // Verify user was redirected away from user login to an accessible page.
624
    $this->assertResponse(200);
625

    
626
    // Verify the message.
627
    $this->assertRaw(t('There is already an existing account associated with the OpenID identifier that you have provided.'), 'Message that OpenID identifier must be updated manually was displayed.');
628
  }
629

    
630
  /**
631
   * Test OpenID auto transition with e-mail.
632
   */
633
  function testStrippedFragmentAccountAutoUpdateSreg() {
634
    $this->drupalLogin($this->web_user);
635

    
636
    // Use a User-supplied Identity that is the URL of an XRDS document.
637
    $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE, 'fragment' => $this->randomName()));
638
    $identity_stripped = preg_replace('/#.*/', '', $identity);
639

    
640
    // Add invalid identifier to the authmap (identifier has stripped fragment).
641
    $this->addIdentity($identity_stripped);
642
    $this->drupalLogout();
643

    
644
    // Test logging in via the login form, provider will respond with full
645
    // identifier (including fragment) but with different email, so we can't
646
    // provide auto-update.
647
    variable_set('openid_test_response', array(
648
      'openid.claimed_id' => $identity,
649
      'openid.sreg.nickname' => $this->web_user->name,
650
      'openid.sreg.email' => $this->web_user->mail));
651

    
652
    $this->submitLoginForm($identity_stripped);
653

    
654
    // Verify user was redirected away from user login to an accessible page.
655
    $this->assertResponse(200);
656

    
657
    // Verify the message.
658
    $this->assertRaw(t('New OpenID identifier %identity was added as a replacement for invalid identifier %invalid_identity.', array('%invalid_identity' => $identity_stripped, '%identity' => $identity)), 'Message that OpenID identifier was added automatically was displayed.');
659
  }
660
}
661

    
662
/**
663
 * Test internal helper functions.
664
 */
665
class OpenIDTestCase extends DrupalWebTestCase {
666
  public static function getInfo() {
667
    return array(
668
      'name' => 'OpenID helper functions',
669
      'description' => 'Test OpenID helper functions.',
670
      'group' => 'OpenID'
671
    );
672
  }
673

    
674
  function setUp() {
675
    parent::setUp('openid');
676
    module_load_include('inc', 'openid');
677
  }
678

    
679
  /**
680
   * Test _openid_dh_XXX_to_XXX() functions.
681
   */
682
  function testConversion() {
683
    $this->assertIdentical(_openid_dh_long_to_base64('12345678901234567890123456789012345678901234567890'), 'CHJ/Y2mq+DyhUCZ0evjH8ZbOPwrS', '_openid_dh_long_to_base64() returned expected result.');
684
    $this->assertIdentical(_openid_dh_base64_to_long('BsH/g8Nrpn2dtBSdu/sr1y8hxwyx'), '9876543210987654321098765432109876543210987654321', '_openid_dh_base64_to_long() returned expected result.');
685

    
686
    $this->assertIdentical(_openid_dh_long_to_binary('12345678901234567890123456789012345678901234567890'), "\x08r\x7fci\xaa\xf8<\xa1P&tz\xf8\xc7\xf1\x96\xce?\x0a\xd2", '_openid_dh_long_to_binary() returned expected result.');
687
    $this->assertIdentical(_openid_dh_binary_to_long("\x06\xc1\xff\x83\xc3k\xa6}\x9d\xb4\x14\x9d\xbb\xfb+\xd7/!\xc7\x0c\xb1"), '9876543210987654321098765432109876543210987654321', '_openid_dh_binary_to_long() returned expected result.');
688
  }
689

    
690
  /**
691
   * Test _openid_dh_xorsecret().
692
   */
693
  function testOpenidDhXorsecret() {
694
    $this->assertEqual(_openid_dh_xorsecret('123456790123456790123456790', "abc123ABC\x00\xFF"), "\xa4'\x06\xbe\xf1.\x00y\xff\xc2\xc1", '_openid_dh_xorsecret() returned expected result.');
695
  }
696

    
697
  /**
698
   * Test _openid_signature().
699
   */
700
  function testOpenidSignature() {
701
    // Test that signature is calculated according to OpenID Authentication 2.0,
702
    // section 6.1. In the following array, only the two first entries should be
703
    // included in the calculation, because the substring following the period
704
    // is mentioned in the third argument for _openid_signature(). The last
705
    // entry should not be included, because it does not start with "openid.".
706
    $response = array(
707
      'openid.foo' => 'abc1',
708
      'openid.bar' => 'abc2',
709
      'openid.baz' => 'abc3',
710
      'foobar.foo' => 'abc4',
711
    );
712
    $association = new stdClass();
713
    $association->mac_key = "1234567890abcdefghij\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9";
714
    $this->assertEqual(_openid_signature($association, $response, array('foo', 'bar')), 'QnKZQzSFstT+GNiJDFOptdcZjrc=', 'Expected signature calculated.');
715
  }
716

    
717
  /**
718
   * Test _openid_is_xri().
719
   */
720
  function testOpenidXRITest() {
721
    // Test that the XRI test is according to OpenID Authentication 2.0,
722
    // section 7.2. If the user-supplied string starts with xri:// it should be
723
    // stripped and the resulting string should be treated as an XRI when it
724
    // starts with "=", "@", "+", "$", "!" or "(".
725
    $this->assertTrue(_openid_is_xri('xri://=foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
726
    $this->assertTrue(_openid_is_xri('xri://@foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
727
    $this->assertTrue(_openid_is_xri('xri://+foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
728
    $this->assertTrue(_openid_is_xri('xri://$foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme.');
729
    $this->assertTrue(_openid_is_xri('xri://!foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme..');
730
    $this->assertTrue(_openid_is_xri('xri://(foo'), '_openid_is_xri() returned expected result for an xri identifier with xri scheme..');
731

    
732
    $this->assertTrue(_openid_is_xri('=foo'), '_openid_is_xri() returned expected result for an xri identifier.');
733
    $this->assertTrue(_openid_is_xri('@foo'), '_openid_is_xri() returned expected result for an xri identifier.');
734
    $this->assertTrue(_openid_is_xri('+foo'), '_openid_is_xri() returned expected result for an xri identifier.');
735
    $this->assertTrue(_openid_is_xri('$foo'), '_openid_is_xri() returned expected result for an xri identifier.');
736
    $this->assertTrue(_openid_is_xri('!foo'), '_openid_is_xri() returned expected result for an xri identifier.');
737
    $this->assertTrue(_openid_is_xri('(foo'), '_openid_is_xri() returned expected result for an xri identifier.');
738

    
739
    $this->assertFalse(_openid_is_xri('foo'), '_openid_is_xri() returned expected result for an http URL.');
740
    $this->assertFalse(_openid_is_xri('xri://foo'), '_openid_is_xri() returned expected result for an http URL.');
741
    $this->assertFalse(_openid_is_xri('http://foo/'), '_openid_is_xri() returned expected result for an http URL.');
742
    $this->assertFalse(_openid_is_xri('http://example.com/'), '_openid_is_xri() returned expected result for an http URL.');
743
    $this->assertFalse(_openid_is_xri('user@example.com/'), '_openid_is_xri() returned expected result for an http URL.');
744
    $this->assertFalse(_openid_is_xri('http://user@example.com/'), '_openid_is_xri() returned expected result for an http URL.');
745
  }
746

    
747
  /**
748
   * Test openid_normalize().
749
   */
750
  function testOpenidNormalize() {
751
    // Test that the normalization is according to OpenID Authentication 2.0,
752
    // section 7.2 and 11.5.2.
753

    
754
    $this->assertEqual(openid_normalize('$foo'), '$foo', 'openid_normalize() correctly normalized an XRI.');
755
    $this->assertEqual(openid_normalize('xri://$foo'), '$foo', 'openid_normalize() correctly normalized an XRI with an xri:// scheme.');
756

    
757
    $this->assertEqual(openid_normalize('example.com/'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with a missing scheme.');
758
    $this->assertEqual(openid_normalize('example.com'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with a missing scheme and empty path.');
759
    $this->assertEqual(openid_normalize('http://example.com'), 'http://example.com/', 'openid_normalize() correctly normalized a URL with an empty path.');
760

    
761
    $this->assertEqual(openid_normalize('http://example.com/path'), 'http://example.com/path', 'openid_normalize() correctly normalized a URL with a path.');
762

    
763
    $this->assertEqual(openid_normalize('http://example.com/path#fragment'), 'http://example.com/path', 'openid_normalize() correctly normalized a URL with a fragment.');
764
  }
765

    
766
  /**
767
   * Test openid_extract_namespace().
768
   */
769
  function testOpenidExtractNamespace() {
770
    $response = array(
771
      'openid.sreg.nickname' => 'john',
772
      'openid.ns.ext1' => OPENID_NS_SREG,
773
      'openid.ext1.nickname' => 'george',
774
      'openid.ext1.email' => 'george@example.com',
775
      'openid.ns.ext2' => 'http://example.com/ns/ext2',
776
      'openid.ext2.foo' => '123',
777
      'openid.ext2.bar' => '456',
778
      'openid.signed' => 'sreg.nickname,ns.ext1,ext1.email,ext2.foo',
779
    );
780

    
781
    $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', NULL, FALSE);
782
    $this->assertEqual($values, array(), 'Nothing found for unused namespace.');
783

    
784
    $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', 'sreg', FALSE);
785
    $this->assertEqual($values, array('nickname' => 'john'), 'Value found for fallback prefix.');
786

    
787
    $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', FALSE);
788
    $this->assertEqual($values, array('nickname' => 'george', 'email' => 'george@example.com'), 'Namespace takes precedence over fallback prefix.');
789

    
790
    // ext1.email is signed, but ext1.nickname is not.
791
    $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
792
    $this->assertEqual($values, array('email' => 'george@example.com'), 'Unsigned namespaced fields ignored.');
793

    
794
    $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', FALSE);
795
    $this->assertEqual($values, array('foo' => '123', 'bar' => '456'), 'Unsigned fields found.');
796

    
797
    // ext2.foo and ext2.bar are ignored, because ns.ext2 is not signed. The
798
    // fallback prefix is not used, because the namespace is specified.
799
    $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', TRUE);
800
    $this->assertEqual($values, array(), 'Unsigned fields ignored.');
801
  }
802
}