# HG changeset patch # User Dan Fuhry # Date 1460032178 0 # Node ID 29e93991703b7758bef77980ac1bdcd8561500dc Initial commit diff -r 000000000000 -r 29e93991703b HTTPAuth.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPAuth.php Thu Apr 07 12:29:38 2016 +0000 @@ -0,0 +1,441 @@ +attachHook('compile_template', 'http_auth_attach_headers($this);'); + $plugins->attachHook('login_form_html', 'http_auth_login_html();'); +} + +function http_auth_attach_headers(&$template) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $template->add_header(''); +} + +function http_auth_login_html() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + global $output; + + ob_end_clean(); + + $return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page(); + $qs = ( isset($_GET['level']) ) ? 'level=' . $_GET['level'] : ''; + + $uri = makeUrlNS('Special', 'LoginHTTP/' . $return, $qs); + + redirect($uri, '', '', 0); + exit; +} + +// Registration blocking hook +if ( getConfig('http_auth_disable_local', 0) == 1 ) +{ + $plugins->attachHook('ucp_register_validate', 'http_auth_reg_block($error);'); +} + +function http_auth_reg_block(&$error) +{ + $error = 'Registration on this website is disabled because HTTP authentication is configured. Please log in using a valid username and password, and an account will be created for you automatically.'; +} + +$plugins->attachHook('session_started', 'http_auth_add_special();'); + +function http_auth_add_special() +{ + register_special_page('LoginHTTP', 'Login with HTTP Authentication', true); +} + +function page_Special_LoginHTTP() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + global $output; + + if ( isset($_GET['level']) ) { + $result = array('result' => 'error'); + + if ( !empty($_SERVER['REMOTE_USER']) ) { + $level = intval($_GET['level']); + if ( $level > USER_LEVEL_MEMBER ) { + $username = $db->escape(strtolower($_SERVER['REMOTE_USER'])); + + $q = $db->sql_query("SELECT user_id, password, user_level FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() == 1 ) { + $row = $db->fetchrow(); + + if ( $row['user_level'] < $level ) { + die_friendly('Access denied', '

Not permitted to authenticate at this level.

'); + } + + $session->register_session($row['user_id'], $_SERVER['REMOTE_USER'], $row['password'], $level, $remember); + + $result = array( + 'result' => 'success', + 'sid' => $session->sid_super + ); + } + + $db->free_result(); + } + } + + if ( isset($_GET['ajax']) ) { + $output = new Output_Naked; + header('Content-type: text/javascript'); + echo json_encode($result); + + return; + } + } + else + { + if ( empty($_SERVER['REMOTE_USER']) ) { + die_friendly('No HTTP authentication supplied', '

This site is configured for HTTP authentication, but none was supplied by the webserver software. Please verify your webserver configuration.

'); + } + + http_auth_do_login(); + } + + $return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page(); + redirect(makeUrl($return), 'Logged in', 'You have successfully logged in using HTTP authentication. You will be momentarily taken to your destination.', 3); +} + +function http_auth_do_login() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $user = $_SERVER['REMOTE_USER']; + + $username = $db->escape(strtolower($user)); + + $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() < 1 ) + { + // This user doesn't exist. + // Is creating it our job? + if ( getConfig('http_auth_disable_local', 0) == 1 ) + { + // Yep, register him + $email = strtolower($user) . '@' . getConfig('http_auth_email_domain', 'localhost'); + $random_pass = md5(microtime() . mt_rand()); + // load the language + $session->register_guest_session(); + $reg_result = $session->create_user($user, $random_pass, $email); + if ( $reg_result != 'success' ) + { + // o_O + // Registration failed. + die_friendly('HTTP authentication error', '

Your username and password were valid, but there was a problem instanciating your local user account: ' . $reg_result . '.

'); + } + // Get user ID + $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); + if ( !$q ) + $db->_die(); + if ( $db->numrows() < 1 ) { + die_friendly('HTTP authentication error', '

Your username and password were valid, but there was a problem getting your user ID.

'); + } + $row = $db->fetchrow(); + $db->free_result(); + // Quick - lock the account + $q = $db->sql_query('UPDATE ' . table_prefix . "users SET password = 'Locked by HTTP auth plugin', password_salt = 'Locked by HTTP auth plugin' WHERE user_id = {$row['user_id']};"); + if ( !$q ) + $db->_die(); + + $row['password'] = 'Locked by HTTP auth plugin'; + } + else + { + // Nope. Just let Enano fail it properly. + die_friendly('User does not exist', '

You\'ve attempted to log in with an account that doesn\'t exist, and the HTTP Authentication plugin is not configured to auto-create new accounts.

'); + } + } + else + { + $row = $db->fetchrow(); + $db->free_result(); + } + + $session->register_session($row['user_id'], $user, $row['password'], $level, $remember); +} + +// +// ADMIN +// + +$plugins->attachHook('session_started', 'http_auth_session_hook();'); + +if ( getConfig('http_auth_disable_local', 0) == 1 ) +{ + $plugins->attachHook('common_post', 'http_auth_tou_hook();'); +} + +function http_auth_session_hook() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + // Register the admin page + $paths->addAdminNode('adm_cat_security', 'HTTP Authentication', 'HTTPAuthConfig'); + + // Disable password change + if ( getConfig('http_auth_disable_local', 0) == 1 && $session->user_level < USER_LEVEL_ADMIN ) + { + $link_text = getConfig('http_auth_password_text', false); + if ( empty($link_text) ) + $link_text = false; + $link_url = str_replace('%u', $session->username, getConfig('http_auth_password_url', '')); + if ( empty($link_url) ) + $link_url = false; + $session->disable_password_change($link_url, $link_text); + } +} + +function clean_server_redirect_vars() +{ + foreach ( $_SERVER as $key => $value ) { + if ( preg_match($regexp = '/^(REDIRECT_)*/', $key) ) + { + $newkey = preg_replace($regexp, '', $key); + if ( !isset($_SERVER[$newkey]) ) + { + $_SERVER[$newkey] = $value; + } + } + } +} + +function http_auth_tou_hook() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + // Are we supposed to fail if no authentication information is presented? + // first strip REDIRECT_* from $_SERVER variables + clean_server_redirect_vars(); + + if ( getConfig('http_auth_mode', 'guest') === 'noguest' && empty($_SERVER['REMOTE_USER']) ) + { + die_friendly('No authentication provided', '

This Enano website is configured to require HTTP authentication for all pages, but none was provided by the webserver software. Please check your webserver configuration.

'); + } + + if ( !empty($_SERVER['REMOTE_USER']) && !$session->user_logged_in && !in_array($paths->page, array('Special:Login', 'Special:LoginHTTP', 'Special:Logout')) ) { + http_auth_do_login(); + redirect($paths->page, '', '', 0); + } + + // Are we pending TOU acceptance? + if ( $session->user_logged_in && !$session->on_critical_page() && trim(getConfig('register_tou', '')) != '' ) + { + $q = $db->sql_query('SELECT account_active FROM ' . table_prefix . "users WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + + list($active) = $db->fetchrow_num(); + $db->free_result(); + if ( $active == 1 ) + { + // Pending TOU accept + // Basically, what we do here is force the user to accept the TOU and record it by setting account_active to 2 instead of a 1 + // A bit of a hack, but hey, it works, at least in 1.1.8. + // In 1.1.7, it just breaks your whole account, and $session->on_critical_page() is broken in 1.1.7 so you won't even be able + // to go the admin CP and re-activate yourself. Good times... erhm, sorry. + + if ( isset($_POST['tou_agreed']) && $_POST['tou_agreed'] === 'I accept the terms and conditions displayed on this site' ) + { + // Accepted + $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 2 WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die(); + + return true; + } + + global $output, $lang; + $output->set_title('Terms of Use'); + $output->header(); + + ?> +

Please read and accept the following terms:

+ +
+ +
+ +
+

+ +

+

+ +

+
+ + footer(); + + $db->close(); + exit; + } + } +} + +function page_Admin_HTTPAuthConfig() +{ + // Security check + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $session->auth_level < USER_LEVEL_ADMIN ) + return false; + + if ( isset($_POST['submit']) ) + { + setConfig('http_auth_enable', isset($_POST['http_auth_enable']) ? '1' : '0'); + setConfig('http_auth_disable_local', isset($_POST['http_auth_disable_local']) ? '1' : '0'); + setConfig('http_auth_mode', isset($_POST['http_auth_mode']) && in_array($_POST['http_auth_mode'], array('guest', 'noguest')) ? $_POST['http_auth_mode'] : 'guest'); + setConfig('http_auth_password_text', $_POST['http_auth_password_text']); + setConfig('http_auth_password_url', $_POST['http_auth_password_url']); + setConfig('http_auth_email_domain', $_POST['http_auth_email_domain']); + + echo '
Your changes have been saved.
'; + } + + acp_start_form(); + ?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ HTTP Authentication Configuration +
+ Enable HTTP authentication: + + +
+ Enforce HTTP for single-sign-on:
+ Use this option to force HTTP passwords and accounts to be used, regardless of local accounts, except for administrators. +
+ +
+ Guest access mode:
+ You can allow guests to browse the site without logging in, and configure your webserver to require authentication only on the login page URL given below. Or, you can require authentication across the whole site. In the latter case, if the webserver fails to provide any authentication state, page loads will fail. +
+ + + +
+ Login page URL: + + +
+ Set this URL to require authentication in your webserver's configuration. +
+ E-mail address domain for autoregistered users:
+ When a user is automatically registered, this domain will be used as the domain for their e-mail address. This way, activation e-mails will + (ideally) reach the user. +
+ +
+ External password management link:
+ Enter a URL here to link to from Enano's Change Password page. Leave blank to not display a link. The text "%u" will be replaced with the user's username. +
+ Link text:
+ Link URL: +
+ +
+
+ '; +} diff -r 000000000000 -r 29e93991703b httpauth/login-hook.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/httpauth/login-hook.js Thu Apr 07 12:29:38 2016 +0000 @@ -0,0 +1,28 @@ +addOnloadHook(function() + { + attachHook('login_build_form', 'http_auth_login_hook(table, data);'); + }); + +function http_auth_login_hook(table, data) +{ + if ( window.shift ) + return; + + if (window.logindata.user_level <= USER_LEVEL_MEMBER) + { + window.location = makeUrlNS('Special', 'LoginHTTP'); + } + else + { + // re-auth + ajaxGet(makeUrlNS('Special', 'LoginHTTP', 'ajax&level=' + window.logindata.user_level), function(xhr) + { + if ( xhr.readyState == 4 && xhr.status == 200 ) { + var result = JSON.parse(xhr.responseText); + if ( result.result == 'success' ) { + window.logindata.successfunc(result.sid); + } + } + }); + } +}