cms/drupal/modules/openid/openid.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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 }