View source
<?php
function bakery_menu() {
$items = array();
$items['admin/config/system/bakery'] = array(
'title' => 'Bakery',
'access arguments' => array(
'administer bakery',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'bakery_settings',
),
'file' => 'bakery.pages.inc',
'description' => 'Infrastructure-wide single-sign-on system options.',
);
if (variable_get('bakery_is_master', 0)) {
$items['bakery/register'] = array(
'title' => 'Register',
'access callback' => 'user_is_anonymous',
'page callback' => 'bakery_register_handler',
'type' => MENU_CALLBACK,
);
$items['bakery/login'] = array(
'title' => 'Login',
'access callback' => 'user_is_anonymous',
'page callback' => 'bakery_login_handler',
'type' => MENU_CALLBACK,
);
}
else {
$items['bakery/register'] = array(
'title' => 'Register',
'access callback' => TRUE,
'page callback' => 'bakery_register_return',
'type' => MENU_CALLBACK,
);
$items['bakery/login'] = array(
'title' => 'Login',
'access callback' => TRUE,
'page callback' => 'bakery_login_return',
'type' => MENU_CALLBACK,
);
$items['admin/config/people/bakery'] = array(
'title' => 'Pull Bakery user',
'description' => 'Request an account from the master site',
'access arguments' => array(
'administer users',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'bakery_pull_form',
),
'type' => MENU_NORMAL_ITEM,
);
}
return $items;
}
function bakery_translated_menu_link_alter(&$item, $map) {
if ($item['href'] == 'bakery') {
$destination = drupal_get_destination();
$item['localized_options']['query'] = $destination;
}
}
function bakery_permission() {
return array(
'administer bakery' => array(
'title' => t('Administer Bakery'),
),
'bypass bakery' => array(
'title' => t('Bypass Bakery'),
'description' => t('Bypass SSO enforcement policy and allow a user to log in without a valid SSO cookie'),
'restrict access' => TRUE,
),
);
}
function bakery_get_bakery() {
$context_set =& drupal_static(__FUNCTION__, FALSE);
if (!$context_set) {
$config = array(
'is_master' => variable_get('bakery_is_master', 0),
'domain' => variable_get('bakery_domain', 'http://example.com'),
'key' => variable_get('bakery_key', ''),
'sso_cookie' => _bakery_cookie_name('SSO'),
'sub_cookie' => _bakery_cookie_name('SUB'),
'lifetime' => variable_get('bakery_freshness', 3600),
'expiration' => variable_get('bakery_expiration', 3600 * 24 * 7),
'debug' => variable_get('bakery_debug', FALSE),
);
$context_set = TRUE;
return Bakery::instance($config);
}
return Bakery::instance();
}
function bakery_user_login(&$edit, $account) {
if (variable_get('bakery_is_master', 0) && isset($account->uid)) {
$bakery = bakery_get_bakery();
$params = array(
'name' => $account->name,
'mail' => $account->mail,
'init' => _bakery_init_field($account->uid),
'uid' => $account->uid,
);
$bakery
->setSsoCookie($params);
}
}
function bakery_user_logout($account) {
global $user;
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSsoCookie();
if ($user->uid && !is_null($cookie) && $cookie !== FALSE) {
$bakery
->deleteSsoCookie();
}
}
function bakery_user_update(&$edit, $account, $category) {
global $user;
if (variable_get('bakery_is_master', 0) && isset($_SESSION['bakery'])) {
if ($user->uid === $account->uid) {
$bakery = bakery_get_bakery();
$params = array(
'name' => $account->name,
'mail' => $account->mail,
'init' => _bakery_init_field($account->uid),
'uid' => $account->uid,
);
$bakery
->setSsoCookie($params);
}
}
}
function bakery_boot() {
if (drupal_is_cli()) {
return;
}
global $user;
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSsoCookie($user);
if ($cookie) {
_bakery_handle_sso($cookie);
}
else {
if ($cookie === FALSE) {
$bakery
->deleteSsoCookie();
}
if (!$cookie && $user->uid > 1) {
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
if (!user_access('bypass bakery')) {
watchdog('bakery', 'Logging out the user with the bad cookie.');
_bakery_user_logout();
}
}
}
}
function bakery_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
case 'user_profile_form':
case 'user_edit_form':
if (!variable_get('bakery_is_master', 0) && !user_access('administer users')) {
$init_url = _bakery_init_field_url($form['#user']->init);
$index = key($form);
if (isset($form['account'])) {
drupal_set_message(t('You can change the name, mail, and password <a href="!url">at the master site</a>.', array(
'!url' => check_url($init_url),
)), 'status', FALSE);
$form['account']['#access'] = FALSE;
$form['account']['name']['#access'] = FALSE;
$form['account']['pass']['#access'] = FALSE;
$form['account']['mail']['#access'] = FALSE;
}
foreach (variable_get('bakery_supported_fields', array(
'mail' => 'mail',
'name' => 'name',
)) as $type => $value) {
if ($value) {
switch ($type) {
case 'mail':
case 'name':
break;
case 'picture':
if (isset($form['picture'])) {
$form['picture']['picture_delete']['#access'] = FALSE;
$form['picture']['picture_upload']['#access'] = FALSE;
$form['picture']['#description'] = t('You can change the image <a href="!url">at the master site</a>.', array(
'!url' => check_url($init_url),
));
}
break;
case 'language':
if (isset($form['locale'][$type])) {
$form['locale'][$type]['#disabled'] = TRUE;
$form['locale'][$type]['#description'] .= ' ' . t('You can change the language setting <a href="!url">at the master site</a>.', array(
'!url' => check_url($init_url),
));
}
break;
case 'signature':
if (isset($form['signature_settings'][$type])) {
$form['signature_settings'][$type]['#disabled'] = TRUE;
$form['signature_settings'][$type]['#description'] .= ' ' . t('You can change the signature <a href="!url">at the master site</a>.', array(
'!url' => check_url($init_url),
));
}
break;
default:
if (isset($form[$type])) {
$form[$type]['#disabled'] = TRUE;
}
if (isset($form[$type][$type])) {
$form[$type][$type]['#disabled'] = TRUE;
$form[$type][$type]['#description'] .= ' ' . t('You can change this setting <a href="!url">at the master site</a>.', array(
'!url' => check_url($init_url),
));
}
break;
}
}
}
}
break;
case 'user_register_form':
if (!variable_get('bakery_is_master', FALSE)) {
if (arg(0) == 'admin') {
$form['account']['bakery_help'] = array(
'#value' => t('<strong>Note:</strong> Only use this form to create accounts for users who exist on <a href="!url">@master</a> and not on this site. Be sure to use the exact same username and e-mail for the account here that they have on @master.', array(
'!url' => variable_get('bakery_master', 'http://drupal.org'),
'@master' => variable_get('bakery_master', 'http://drupal.org'),
)),
'#weight' => -100,
);
}
else {
if (isset($_SESSION['bakery']['register'])) {
$form['account']['name']['#default_value'] = $_SESSION['bakery']['register']['name'];
$form['account']['mail']['#default_value'] = $_SESSION['bakery']['register']['mail'];
unset($_SESSION['bakery']['register']);
}
$form['#submit'] = array(
'_bakery_register_submit',
);
}
}
break;
case 'user_pass':
if (!variable_get('bakery_is_master', FALSE)) {
array_unshift($form['#validate'], '_bakery_pass_validate');
}
break;
case 'user_pass_reset':
if (!variable_get('bakery_is_master', FALSE)) {
$form['#submit'] = array(
'_bakery_reset_submit',
);
unset($form['#action']);
}
break;
case 'user_login_block':
case 'user_login':
if (!variable_get('bakery_is_master', FALSE)) {
$form['#validate'] = array_diff($form['#validate'], array(
'user_login_authenticate_validate',
'user_login_final_validate',
));
$form['#submit'] = array(
'_bakery_login_submit',
);
}
break;
default:
break;
}
}
function _bakery_save_destination_param($form, &$data) {
if (strpos($form['#action'], 'destination=') !== FALSE) {
parse_str(parse_url($form['#action'], PHP_URL_QUERY), $url_args);
if (!empty($url_args['destination'])) {
$data['destination'] = $url_args['destination'];
}
}
}
function _bakery_register_submit($form, &$form_state) {
global $base_url;
$allowed = array(
'name',
'mail',
'pass',
'timezone',
);
foreach ($form_state['values'] as $key => $value) {
if (!in_array($key, $allowed)) {
unset($form_state['values'][$key]);
}
}
form_state_values_clean($form_state);
$data = $form_state['values'];
_bakery_save_destination_param($form, $data);
unset($_GET['destination']);
$_SESSION['bakery']['register'] = array(
'name' => $data['name'],
'mail' => $data['mail'],
);
$bakery = bakery_get_bakery();
$bakery
->setSubCookie($data['name'], $data, $base_url . '/');
drupal_goto(variable_get('bakery_master', 'http://drupal.org/') . 'bakery/register');
}
function _bakery_login_submit($form, &$form_state) {
global $base_url;
$allowed = array(
'name',
'pass',
'op',
);
foreach ($form_state['values'] as $key => $value) {
if (!in_array($key, $allowed)) {
unset($form_state['values'][$key]);
}
}
$data = $form_state['values'];
_bakery_save_destination_param($form, $data);
unset($_GET['destination']);
$data['query'] = drupal_get_query_parameters();
$bakery = bakery_get_bakery();
$bakery
->setSubCookie($data['name'], $data, $base_url . '/');
drupal_goto(variable_get('bakery_master', 'http://drupal.org/') . 'bakery/login');
}
function bakery_register_handler() {
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSubCookie();
if (!$cookie) {
return MENU_ACCESS_DENIED;
}
if (variable_get('user_register', 1)) {
$data = array();
$errors = array();
$name = trim($cookie['data']['name']);
$mail = trim($cookie['data']['mail']);
$account = user_load_by_mail($mail);
if ($account) {
$errors['mail'] = 1;
}
else {
$account = user_load_by_name($name);
if ($account) {
$errors['name'] = 1;
}
}
}
else {
watchdog('bakery', 'Master Bakery site user registration is disabled but users are trying to register from a subsite.', array(), WATCHDOG_ERROR);
$errors['register'] = 1;
}
if (empty($errors)) {
$userinfo = $cookie['data'];
if (!$cookie['data']['pass']) {
$pass = user_password();
}
else {
$pass = $cookie['data']['pass'];
}
$userinfo['name'] = $name;
$userinfo['mail'] = $mail;
$userinfo['pass'] = $pass;
$userinfo['init'] = $mail;
$userinfo['status'] = 1;
$userinfo['authname_bakery'] = $name;
$account = user_save('', $userinfo);
$data['uid'] = $account->uid;
$data['mail'] = $mail;
watchdog('user', 'New external user: %name using module bakery from slave !slave.', array(
'%name' => $account->name,
'!slave' => $cookie['slave'],
), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
if (!variable_get('user_email_verification', TRUE)) {
$params = array(
'name' => $account->name,
'mail' => $account->mail,
'init' => _bakery_init_field($account->uid),
'uid' => $account->uid,
);
$bakery
->setSsoCookie($params);
bakery_user_external_login($account);
}
else {
$errors['validate'] = 1;
}
}
else {
session_destroy();
}
$data['errors'] = $errors;
$data['name'] = $name;
if (isset($cookie['data']['destination'])) {
$data['destination'] = $cookie['data']['destination'];
}
$bakery
->setSubCookie($name, $data, $cookie['slave']);
drupal_goto($cookie['slave'] . 'bakery/register');
}
function bakery_register_return() {
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSubCookie();
if (!$cookie) {
return MENU_ACCESS_DENIED;
}
$bakery
->deleteSubCookie();
if (empty($cookie['data']['destination'])) {
$destination = '<front>';
}
else {
$destination = $cookie['data']['destination'];
}
$errors = $cookie['data']['errors'];
if (empty($errors)) {
drupal_set_message(t('Registration successful. You are now logged in.'));
drupal_goto($destination);
}
else {
if (!empty($errors['register'])) {
drupal_set_message(t('Registration is not enabled on @master. Please contact a site administrator.', array(
'@master' => variable_get('bakery_master', 'http://drupal.org/'),
)), 'error');
watchdog('bakery', 'Master Bakery site user registration is disabled', array(), WATCHDOG_ERROR);
}
if (!empty($errors['validate'])) {
$new = array(
'name' => $cookie['name'],
'mail' => $cookie['data']['mail'],
'init' => _bakery_init_field($cookie['data']['uid']),
'status' => 1,
'pass' => user_password(),
);
$account = user_save(new stdClass(), $new);
_user_mail_notify('register_no_approval_required', $account);
unset($_SESSION['bakery']['register']);
drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
}
if (!empty($errors['name'])) {
drupal_set_message(t('Name is already taken.'), 'error');
}
if (!empty($errors['mail'])) {
drupal_set_message(t('E-mail address is already registered.'), 'error');
}
if (!empty($errors['mail_denied'])) {
drupal_set_message(t('The e-mail address has been denied access..'), 'error');
}
if (!empty($errors['name_denied'])) {
drupal_set_message(t('The name has been denied access..'), 'error');
}
drupal_goto('user/register');
}
}
function bakery_login_handler() {
global $user;
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSubCookie();
if (!$cookie) {
return MENU_ACCESS_DENIED;
}
$cookie['data'] += array(
'query' => array(),
);
$errors = array();
$name = trim($cookie['data']['name']);
$pass = trim($cookie['data']['pass']);
$form_state = array();
$form_state['values'] = $cookie['data'];
drupal_form_submit('user_login', $form_state);
$errors = form_get_errors();
if (empty($errors)) {
$account = user_load_by_name($name);
if (isset($account->uid)) {
if (drupal_is_denied('user', $account->mail)) {
$errors['name'] = t('The name %name is registered using a reserved e-mail address and therefore could not be logged in.', array(
'%name' => $name,
));
}
else {
$params = array(
'name' => $account->name,
'mail' => $account->mail,
'init' => _bakery_init_field($account->uid),
'uid' => $account->uid,
);
$bakery
->setSsoCookie($params);
$user = $account;
$edit = array(
'name' => $user->name,
);
bakery_user_authenticate_finalize($edit);
}
}
else {
$errors['incorrect-credentials'] = 1;
}
}
if (!empty($errors)) {
watchdog('user', 'Login attempt failed for %user.', array(
'%user' => $name,
));
drupal_get_messages();
}
$data = array(
'errors' => $errors,
'name' => $name,
);
if (isset($cookie['data']['destination'])) {
$data['destination'] = $cookie['data']['destination'];
}
$data['query'] = $cookie['data']['query'];
$bakery
->setSubCookie($name, $data, $cookie['slave']);
drupal_goto($cookie['slave'] . '/bakery/login');
}
function bakery_login_return() {
global $user;
$bakery = bakery_get_bakery();
$cookie = $bakery
->validateSubCookie();
if ($cookie) {
$cookie['data'] += array(
'query' => array(),
);
$bakery
->deleteSubCookie();
if (!empty($cookie['data']['errors'])) {
$errors = $cookie['data']['errors'];
if (!empty($errors['incorrect-credentials'])) {
drupal_set_message(t('Sorry, unrecognized username or password.'), 'error');
}
elseif (!empty($errors['name'])) {
drupal_set_message(filter_xss($errors['name']), 'error');
}
}
$options = array(
'query' => $cookie['data']['query'],
);
if (empty($cookie['data']['destination'])) {
drupal_goto('user', $options);
}
else {
$destination = $cookie['data']['destination'];
if (($pos = strpos($cookie['data']['destination'], '?')) !== FALSE) {
$destination = substr($cookie['data']['destination'], 0, $pos);
$options['query'] += drupal_get_query_array(substr($cookie['data']['destination'], $pos + 1));
}
drupal_goto($destination, $options);
}
}
elseif (user_is_logged_in()) {
drupal_goto();
}
drupal_access_denied();
}
function _bakery_cookie_name($type = 'SSO') {
if (ini_get('session.cookie_secure')) {
$type .= 'SSL';
}
$extension = variable_get('bakery_cookie_extension', '');
$type .= $extension;
return 'BAKERY' . $type;
}
function _bakery_handle_sso($cookie) {
global $user;
if ($user->uid && $cookie['name'] !== $user->name) {
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
_bakery_user_logout();
}
elseif ($user->uid > 0) {
return;
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$account = user_load_multiple(array(), array(
'name' => $cookie['name'],
'mail' => $cookie['mail'],
));
$account = reset($account);
if (!$account && !variable_get('bakery_is_master', 0) && $cookie['master']) {
$count = db_select('users', 'u')
->fields('u', array(
'uid',
))
->condition('init', $cookie['init'])
->countQuery()
->execute()
->fetchField();
if ($count > 1) {
watchdog('bakery', 'Account uniqueness problem: Multiple users found with init %init.', array(
'%init' => $cookie['init'],
), WATCHDOG_ERROR);
drupal_set_message(t('Account uniqueness problem detected. <a href="@contact">Please contact the site administrator.</a>', array(
'@contact' => variable_get('bakery_master', 'http://drupal.org/') . 'contact',
)), 'error');
}
if ($count == 1) {
$account = user_load_multiple(array(), array(
'init' => $cookie['init'],
));
if (is_array($account)) {
$account = reset($account);
}
if ($account) {
watchdog('bakery', 'Fixing out of sync uid %uid. Changed name %name_old to %name_new, mail %mail_old to %mail_new.', array(
'%uid' => $account->uid,
'%name_old' => $account->name,
'%name_new' => $cookie['name'],
'%mail_old' => $account->mail,
'%mail_new' => $cookie['mail'],
));
user_save($account, array(
'name' => $cookie['name'],
'mail' => $cookie['mail'],
));
$account = user_load_multiple(array(), array(
'name' => $cookie['name'],
'mail' => $cookie['mail'],
));
$account = reset($account);
}
}
}
if (!$account && !variable_get('bakery_is_master', 0) && $cookie['master']) {
if (_bakery_check_account($user->uid, $cookie)) {
$account = bakery_create_account($cookie);
}
else {
drupal_set_message(t('Your user account on %site appears to have problems. Would you like to try to <a href="@url">repair it yourself</a>?', array(
'%site' => variable_get('site_name', 'Drupal'),
'@url' => url('bakery/repair'),
)));
drupal_set_message(filter_xss_admin(variable_get('bakery_help_text', 'Otherwise you can contact the site administrators.')));
$_SESSION['BAKERY_CRUMBLED'] = TRUE;
}
}
if ($account && $cookie['master'] && $account->uid && !variable_get('bakery_is_master', 0) && $account->init != $cookie['init']) {
$count = db_select('users', 'u')
->fields('u', array(
'uid',
))
->condition('init', $cookie['init'], '=')
->countQuery()
->execute()
->fetchField();
if ($count == 0) {
db_update('users')
->fields(array(
'init' => $cookie['init'],
))
->condition('uid', $account->uid)
->execute();
watchdog('bakery', 'uid %uid out of sync. Changed init field from %oldinit to %newinit', array(
'%oldinit' => $account->init,
'%newinit' => $cookie['init'],
'%uid' => $account->uid,
));
}
else {
watchdog('bakery', 'Accounts mixed up! Username %user and init %init disagree with each other!', array(
'%user' => $account->name,
'%init' => $cookie['init'],
), WATCHDOG_CRITICAL);
}
}
if ($account && $user->uid == 0) {
$login = bakery_user_external_login($account);
if ($login) {
$query = drupal_get_query_parameters();
unset($_GET['destination']);
drupal_goto(current_path(), array(
'query' => $query,
));
}
}
}
function _bakery_check_account($uid, $data) {
$checks = TRUE;
$mail_count = db_select('users', 'u')
->fields('u', array(
'uid',
))
->condition('uid', $uid, '!=')
->condition('mail', '', '!=')
->where('LOWER(mail) = LOWER(:mail)', array(
':mail' => $data['mail'],
))
->countQuery()
->execute()
->fetchField();
if ($mail_count > 0) {
$checks = FALSE;
}
$name_count = db_select('users', 'u')
->fields('u', array(
'uid',
))
->condition('uid', $uid, '!=')
->where('LOWER(name) = LOWER(:name)', array(
':name' => $data['name'],
))
->countQuery()
->execute()
->fetchField();
if ($name_count > 0) {
$checks = FALSE;
}
$init_count = db_select('users', 'u')
->fields('u', array(
'uid',
))
->condition('uid', $uid, '!=')
->condition('init', $data['init'], '=')
->where('LOWER(name) = LOWER(:name)', array(
':name' => $data['name'],
))
->countQuery()
->execute()
->fetchField();
if ($init_count > 0) {
$checks = FALSE;
}
return $checks;
}
function bakery_create_account($data) {
$new_account = array(
'name' => $data['name'],
'pass' => user_password(),
'mail' => $data['mail'],
'status' => 1,
'init' => _bakery_init_field($data['uid']),
);
$account = user_save(NULL, $new_account);
return $account;
}
function _bakery_init_field($uid) {
$url = variable_get('bakery_master', 'http://drupal.org/');
$scheme = parse_url($url, PHP_URL_SCHEME);
return str_replace($scheme . '://', '', $url) . 'user/' . $uid . '/edit';
}
function _bakery_init_field_url($init) {
$scheme = parse_url(variable_get('bakery_master', 'http://drupal.org/'), PHP_URL_SCHEME);
return $scheme . '://' . $init;
}
function bakery_user_external_login($account, $edit = array()) {
$form = drupal_get_form('user_login');
$state['values'] = $edit;
if (empty($state['values']['name'])) {
$state['values']['name'] = $account->name;
}
user_login_name_validate($form, $state, (array) $account);
if (form_get_errors()) {
return FALSE;
}
global $user;
$user = $account;
bakery_user_authenticate_finalize($state['values']);
return TRUE;
}
function bakery_user_authenticate_finalize(&$edit) {
global $user;
watchdog('user', 'Session opened for %name.', array(
'%name' => $user->name,
));
$user->login = time();
db_update('users')
->fields(array(
'login' => $user->login,
))
->condition('uid', $user->uid, '=')
->execute();
drupal_session_regenerate();
user_module_invoke('login', $edit, $user);
}
function _bakery_user_logout() {
global $user;
watchdog('user', 'Session closed for %name.', array(
'%name' => $user->name,
));
session_destroy();
module_invoke_all('user_logout', $user);
$user = drupal_anonymous_user();
$get = $_GET;
$destination = !empty($get['q']) ? $get['q'] : '';
unset($get['q']);
$get['no_cache'] = time();
$url = url($destination, array(
'query' => $get,
'absolute' => TRUE,
'alias' => TRUE,
));
$url = str_replace(array(
"\n",
"\r",
), '', $url);
header('Location: ' . $url, TRUE, 307);
exit;
}