Commit 881b8a6a authored by Dries's avatar Dries
Browse files

Issue #817118 by Sylvain Lecoy, bfroehle, Stevel: remove {authmap} and migrate...

Issue #817118 by Sylvain Lecoy, bfroehle, Stevel: remove {authmap} and migrate OpenID entries to their own table.
parent 551868bb
......@@ -54,6 +54,41 @@ function openid_schema() {
'primary key' => array('assoc_handle'),
);
$schema['openid_identities'] = array(
'description' => 'Stores OpenID authentication mapping.',
'fields' => array(
'aid' => array(
'description' => 'Primary Key: Unique authmap ID.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "User's {users}.uid.",
),
'identifier' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'Unique OpenID identifier.',
),
),
'unique keys' => array(
'identifier' => array('identifier'),
),
'primary key' => array('aid'),
'foreign keys' => array(
'user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
);
$schema['openid_nonce'] = array(
'description' => 'Stores received openid.response_nonce per OpenID endpoint URL to prevent replay attacks.',
'fields' => array(
......@@ -123,6 +158,11 @@ function openid_update_last_removed() {
return 6000;
}
/**
* @addtogroup updates-7.x-to-8.x
* @{
*/
/**
* Moves xri_proxy_resolver settings from variable to config.
*
......@@ -133,3 +173,62 @@ function openid_update_8001() {
'xri_proxy_resolver' => 'xri_proxy_resolver',
));
}
/**
* Move authentication mapping to an OpenID managed table.
*/
function openid_update_8002() {
$schema['openid_identities'] = array(
'description' => 'Stores OpenID authentication mapping.',
'fields' => array(
'aid' => array(
'description' => 'Primary Key: Unique authmap ID.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => "User's {users}.uid.",
),
'identifier' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => 'Unique OpenID identifier.',
),
),
'unique keys' => array(
'identifier' => array('identifier'),
),
'primary key' => array('aid'),
'foreign keys' => array(
'user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
);
db_create_table('openid_identities', $schema['openid_identities']);
// Migrate entries from {authmap} to {openid_identities}.
$query = db_select('authmap', 'a')
->condition('module', 'openid');
$query->addField('a', 'uid');
$query->addField('a', 'authname', 'identifier');
db_insert('openid_identities')
->from($query)
->execute();
// Remove old entries in {authmap}.
db_delete('authmap')
->condition('module', 'openid')
->execute();
}
/**
* @} End of "addtogroup updates-7.x-to-8.x".
*/
......@@ -87,6 +87,32 @@ function openid_help($path, $arg) {
}
}
/**
* Fetch a user object by OpenID identifier.
*
* @param $identifier
* The OpenID identifier.
*
* @return
* A fully-loaded user object if the user is found or FALSE if not found.
*/
function openid_external_load($identifier) {
$uid = db_query("SELECT uid FROM {openid_identities} WHERE identifier = :identifier", array(':identifier' => $identifier))->fetchField();
if ($uid) {
return user_load($uid);
}
return FALSE;
}
/**
* Implements hook_user_delete().
*/
function openid_user_delete($account) {
db_delete('openid_identities')
->condition('uid', $account->uid)
->execute();
}
/**
* Implements hook_user_insert().
*/
......@@ -96,7 +122,12 @@ function openid_user_insert($account) {
if (config('user.settings')->get('verify_mail')) {
drupal_set_message(t('Once you have verified your e-mail address, you may log in via OpenID.'));
}
user_set_authmaps($account, array('authname_openid' => $account->openid_claimed_id));
db_insert('openid_identities')
->fields(array(
'uid' => $account->uid,
'identifier' => $account->openid_claimed_id,
))
->execute();
unset($_SESSION['openid']);
unset($account->openid_claimed_id);
}
......@@ -720,7 +751,7 @@ function openid_association($op_endpoint) {
function openid_authentication($response) {
$identity = $response['openid.claimed_id'];
$account = user_external_load($identity);
$account = openid_external_load($identity);
if (isset($account->uid)) {
if (!config('user.settings')->get('verify_mail') || $account->login) {
// Check if user is blocked.
......
......@@ -34,11 +34,10 @@ function openid_user_identities($account) {
$response = openid_complete();
if ($response['status'] == 'success') {
$identity = $response['openid.claimed_id'];
$query = db_insert('authmap')
$query = db_insert('openid_identities')
->fields(array(
'uid' => $account->uid,
'authname' => $identity,
'module' => 'openid',
'identifier' => $identity,
))
->execute();
drupal_set_message(t('Successfully added %identity', array('%identity' => $identity)));
......@@ -49,10 +48,10 @@ function openid_user_identities($account) {
$header = array(t('OpenID'), t('Operations'));
$rows = array();
$result = db_query("SELECT * FROM {authmap} WHERE module='openid' AND uid=:uid", array(':uid' => $account->uid));
$result = db_query("SELECT * FROM {openid_identities} WHERE uid=:uid", array(':uid' => $account->uid));
foreach ($result as $identity) {
$row = array();
$row[] = check_plain($identity->authname);
$row[] = check_plain($identity->identifier);
$links = array();
$links['delete'] = array(
'title' => t('Delete'),
......@@ -96,7 +95,7 @@ function openid_user_add() {
function openid_user_add_validate($form, &$form_state) {
// Check for existing entries.
$claimed_id = openid_normalize($form_state['values']['openid_identifier']);
if (db_query("SELECT authname FROM {authmap} WHERE authname = :authname", (array(':authname' => $claimed_id)))->fetchField()) {
if (db_query("SELECT identifier FROM {openid_identities} WHERE identifier = :identifier", array(':identifier' => $claimed_id))->fetchField()) {
form_set_error('openid_identifier', t('That OpenID is already in use on this site.'));
}
}
......@@ -110,19 +109,18 @@ function openid_user_add_submit($form, &$form_state) {
* Menu callback; Delete the specified OpenID identity from the system.
*/
function openid_user_delete_form($form, $form_state, $account, $aid = 0) {
$authname = db_query("SELECT authname FROM {authmap} WHERE uid = :uid AND aid = :aid AND module = 'openid'", array(
$identifier = db_query("SELECT identifier FROM {openid_identities} WHERE uid = :uid AND aid = :aid", array(
':uid' => $account->uid,
':aid' => $aid,
))
->fetchField();
return confirm_form(array(), t('Are you sure you want to delete the OpenID %authname for %user?', array('%authname' => $authname, '%user' => $account->name)), 'user/' . $account->uid . '/openid');
return confirm_form(array(), t('Are you sure you want to delete the OpenID %identifier for %user?', array('%identifier' => $identifier, '%user' => $account->name)), 'user/' . $account->uid . '/openid');
}
function openid_user_delete_form_submit($form, &$form_state) {
$query = db_delete('authmap')
$query = db_delete('openid_identities')
->condition('uid', $form_state['build_info']['args'][0]->uid)
->condition('aid', $form_state['build_info']['args'][1])
->condition('module', 'openid')
->execute();
if ($query) {
drupal_set_message(t('OpenID deleted.'));
......
<?php
/**
* @file
* Definition of Drupal\user\Tests\UserAuthmapAssignmentTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Unit test for authmap assignment.
*/
class UserAuthmapAssignmentTest extends WebTestBase {
public static function getInfo() {
return array(
'name' => 'Authmap assignment',
'description' => 'Tests that users can be assigned and unassigned authmaps.',
'group' => 'User'
);
}
/**
* Test authmap assignment and retrieval.
*/
function testAuthmapAssignment() {
$account = $this->drupalCreateUser();
// Assign authmaps to the user.
$authmaps = array(
'authname_poll' => 'external username one',
'authname_book' => 'external username two',
);
user_set_authmaps($account, $authmaps);
// Test for expected authmaps.
$expected_authmaps = array(
'external username one' => array(
'poll' => 'external username one',
),
'external username two' => array(
'book' => 'external username two',
),
);
foreach ($expected_authmaps as $authname => $expected_output) {
$this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
}
// Remove authmap for module poll, add authmap for module blog.
$authmaps = array(
'authname_poll' => NULL,
'authname_blog' => 'external username three',
);
user_set_authmaps($account, $authmaps);
// Assert that external username one does not have authmaps.
$remove_username = 'external username one';
unset($expected_authmaps[$remove_username]);
$this->assertFalse(user_get_authmaps($remove_username), format_string('Authmap for %authname was removed.', array('%authname' => $remove_username)));
// Assert that a new authmap was created for external username three, and
// existing authmaps for external username two were unchanged.
$expected_authmaps['external username three'] = array('blog' => 'external username three');
foreach ($expected_authmaps as $authname => $expected_output) {
$this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
}
}
}
......@@ -175,9 +175,6 @@ protected function postDelete($entities) {
db_delete('users_roles')
->condition('uid', array_keys($entities), 'IN')
->execute();
db_delete('authmap')
->condition('uid', array_keys($entities), 'IN')
->execute();
drupal_container()->get('user.data')->delete(NULL, array_keys($entities));
}
}
......@@ -149,7 +149,7 @@ function user_schema() {
);
$schema['authmap'] = array(
'description' => 'Stores distributed authentication mapping.',
'description' => 'Stores distributed authentication mapping. The authmap is deprecated since Drupal 8 and is kept only in a migration data process in mind. Modules that uses the authmap should move their data in custom tables as it will removed in Drupal 9.',
'fields' => array(
'aid' => array(
'description' => 'Primary Key: Unique authmap ID.',
......
......@@ -220,26 +220,6 @@ function user_field_extra_fields() {
return $return;
}
/**
* Fetches a user object based on an external authentication source.
*
* @param string $authname
* The external authentication username.
*
* @return
* A fully-loaded user object if the user is found or FALSE if not found.
*/
function user_external_load($authname) {
$uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchField();
if ($uid) {
return user_load($uid);
}
else {
return FALSE;
}
}
/**
* Loads multiple users based on certain conditions.
*
......@@ -1412,52 +1392,6 @@ function user_page_title($account) {
return is_object($account) ? user_format_name($account) : '';
}
/**
* Discover which external authentication module(s) authenticated a username.
*
* @param $authname
* A username used by an external authentication module.
* @return
* An associative array with module as key and username as value.
*/
function user_get_authmaps($authname = NULL) {
$authmaps = db_query("SELECT module, authname FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchAllKeyed();
return count($authmaps) ? $authmaps : 0;
}
/**
* Save mappings of which external authentication module(s) authenticated
* a user. Maps external usernames to user ids in the users table.
*
* @param $account
* A user object.
* @param $authmaps
* An associative array with a compound key and the username as the value.
* The key is made up of 'authname_' plus the name of the external authentication
* module.
* @see user_external_login_register()
*/
function user_set_authmaps($account, $authmaps) {
foreach ($authmaps as $key => $value) {
$module = explode('_', $key, 2);
if ($value) {
db_merge('authmap')
->key(array(
'uid' => $account->uid,
'module' => $module[1],
))
->fields(array('authname' => $value))
->execute();
}
else {
db_delete('authmap')
->condition('uid', $account->uid)
->condition('module', $module[1])
->execute();
}
}
}
/**
* Form builder; the main user login form.
*
......@@ -1696,36 +1630,6 @@ function user_user_logout($account) {
drupal_static_reset('template_preprocess');
}
/**
* Helper function for authentication modules. Either logs in or registers
* the current user, based on username. Either way, the global $user object is
* populated and login tasks are performed.
*/
function user_external_login_register($name, $module) {
$account = user_external_load($name);
if (!$account) {
// Register this new user.
$account = entity_create('user', array(
'name' => $name,
'pass' => user_password(),
'init' => $name,
'status' => 1,
'access' => REQUEST_TIME
));
$status = $account->save();
// Terminate if an error occurred while saving the account.
if ($status != SAVED_NEW) {
drupal_set_message(t("Error saving user account."), 'error');
return;
}
user_set_authmaps($account, array("authname_$module" => $name));
}
// Log user in.
$form_state['uid'] = $account->uid;
user_login_form_submit(array(), $form_state);
}
/**
* Generates a unique URL for a user to login and reset their password.
*
......
......@@ -390,59 +390,6 @@ function user_views_data() {
),
);
// authmap table
$data['authmap']['table']['group'] = t('User');
$data['authmap']['table']['join'] = array(
// Directly links to users table.
'users' => array(
'left_field' => 'uid',
'field' => 'uid',
),
);
$data['authmap']['aid'] = array(
'title' => t('Authmap ID'),
'help' => t('The Authmap ID.'),
'field' => array(
'id' => 'numeric',
),
'filter' => array(
'id' => 'numeric',
'numeric' => TRUE,
),
'argument' => array(
'id' => 'numeric',
'numeric' => TRUE,
),
);
$data['authmap']['authname'] = array(
'title' => t('Authentication name'),
'help' => t('The unique authentication name.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'numeric',
),
);
$data['authmap']['module'] = array(
'title' => t('Authentication module'),
'help' => t('The name of the module managing the authentication entry.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'string',
),
);
return $data;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment