HTTPAuth.php
changeset 0 29e93991703b
equal deleted inserted replaced
-1:000000000000 0:29e93991703b
       
     1 <?php
       
     2 /**!info**
       
     3 {
       
     4   "Plugin Name"  : "HTTP authentication",
       
     5   "Plugin URI"   : "http://enanocms.org/plugin/httpauth",
       
     6   "Description"  : "Allows authentication to Enano via HTTP authentication.",
       
     7   "Author"       : "Dan Fuhry",
       
     8   "Version"      : "1.0",
       
     9   "Author URI"   : "http://enanocms.org/",
       
    10   "Auth plugin"  : true
       
    11 }
       
    12 **!*/
       
    13 
       
    14 /*
       
    15  * HTTP authentication plugin for Enano
       
    16  * (C) 2014 Dan Fuhry
       
    17  *
       
    18  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
       
    19  * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
       
    20  *
       
    21  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
       
    22  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
       
    23  */
       
    24 
       
    25 if ( getConfig('http_auth_enable', 0) == 1 )
       
    26 {
       
    27   $plugins->attachHook('compile_template', 'http_auth_attach_headers($this);');
       
    28   $plugins->attachHook('login_form_html', 'http_auth_login_html();');
       
    29 }
       
    30 
       
    31 function http_auth_attach_headers(&$template)
       
    32 {
       
    33     global $db, $session, $paths, $template, $plugins; // Common objects
       
    34     
       
    35     $template->add_header('<script type="text/javascript" src="' . scriptPath . '/plugins/httpauth/login-hook.js"></script>');
       
    36 }
       
    37 
       
    38 function http_auth_login_html()
       
    39 {
       
    40 	global $db, $session, $paths, $template, $plugins; // Common objects
       
    41 	
       
    42 	global $output;
       
    43 	
       
    44 	ob_end_clean();
       
    45 	
       
    46 	$return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page();
       
    47 	$qs = ( isset($_GET['level']) ) ? 'level=' . $_GET['level'] : '';
       
    48 	
       
    49 	$uri = makeUrlNS('Special', 'LoginHTTP/' . $return, $qs);
       
    50 	
       
    51 	redirect($uri, '', '', 0);
       
    52 	exit;
       
    53 }
       
    54 
       
    55 // Registration blocking hook
       
    56 if ( getConfig('http_auth_disable_local', 0) == 1 )
       
    57 {
       
    58   $plugins->attachHook('ucp_register_validate', 'http_auth_reg_block($error);');
       
    59 }
       
    60 
       
    61 function http_auth_reg_block(&$error)
       
    62 {
       
    63   $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.';
       
    64 }
       
    65 
       
    66 $plugins->attachHook('session_started', 'http_auth_add_special();');
       
    67  
       
    68 function http_auth_add_special()
       
    69 {
       
    70   register_special_page('LoginHTTP', 'Login with HTTP Authentication', true);
       
    71 }
       
    72  
       
    73 function page_Special_LoginHTTP()
       
    74 {
       
    75   global $db, $session, $paths, $template, $plugins; // Common objects
       
    76   
       
    77   global $output;
       
    78   
       
    79   if ( isset($_GET['level']) ) {
       
    80     $result = array('result' => 'error');
       
    81     
       
    82     if ( !empty($_SERVER['REMOTE_USER']) ) {
       
    83       $level = intval($_GET['level']);
       
    84       if ( $level > USER_LEVEL_MEMBER ) {
       
    85         $username = $db->escape(strtolower($_SERVER['REMOTE_USER']));
       
    86         
       
    87         $q = $db->sql_query("SELECT user_id, password, user_level FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';");
       
    88         if ( !$q )
       
    89           $db->_die();
       
    90         
       
    91         if ( $db->numrows() == 1 ) {
       
    92           $row = $db->fetchrow();
       
    93           
       
    94           if ( $row['user_level'] < $level ) {
       
    95             die_friendly('Access denied', '<p>Not permitted to authenticate at this level.</p>');
       
    96           }
       
    97           
       
    98           $session->register_session($row['user_id'], $_SERVER['REMOTE_USER'], $row['password'], $level, $remember);
       
    99           
       
   100           $result = array(
       
   101             'result' => 'success',
       
   102             'sid' => $session->sid_super
       
   103             );
       
   104         }
       
   105         
       
   106         $db->free_result();
       
   107       }
       
   108     }
       
   109     
       
   110     if ( isset($_GET['ajax']) ) {
       
   111 		$output = new Output_Naked;
       
   112 		header('Content-type: text/javascript');
       
   113 		echo json_encode($result);
       
   114 		
       
   115 		return;
       
   116 	}
       
   117   }
       
   118   else
       
   119   {
       
   120 	  if ( empty($_SERVER['REMOTE_USER']) ) {
       
   121 		die_friendly('No HTTP authentication supplied', '<p>This site is configured for HTTP authentication, but none was supplied by the webserver software. Please verify your webserver configuration.</p>');
       
   122 	  }
       
   123 	  
       
   124 	  http_auth_do_login();
       
   125   }
       
   126   
       
   127   $return = ($goto = $paths->getAllParams()) !== '' ? $goto : get_main_page();
       
   128   redirect(makeUrl($return), 'Logged in', 'You have successfully logged in using HTTP authentication. You will be momentarily taken to your destination.', 3);
       
   129 }
       
   130 
       
   131 function http_auth_do_login()
       
   132 {
       
   133   global $db, $session, $paths, $template, $plugins; // Common objects
       
   134   
       
   135   $user = $_SERVER['REMOTE_USER'];
       
   136   
       
   137   $username = $db->escape(strtolower($user));
       
   138   
       
   139   $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';");
       
   140   if ( !$q )
       
   141     $db->_die();
       
   142   
       
   143   if ( $db->numrows() < 1 )
       
   144   {
       
   145     // This user doesn't exist.
       
   146     // Is creating it our job?
       
   147     if ( getConfig('http_auth_disable_local', 0) == 1 )
       
   148     {
       
   149       // Yep, register him
       
   150       $email = strtolower($user) . '@' . getConfig('http_auth_email_domain', 'localhost');
       
   151       $random_pass = md5(microtime() . mt_rand());
       
   152       // load the language
       
   153       $session->register_guest_session();
       
   154       $reg_result = $session->create_user($user, $random_pass, $email);
       
   155       if ( $reg_result != 'success' )
       
   156       {
       
   157         // o_O
       
   158         // Registration failed.
       
   159         die_friendly('HTTP authentication error', '<p>Your username and password were valid, but there was a problem instanciating your local user account: ' . $reg_result . '.</p>');
       
   160       }
       
   161       // Get user ID
       
   162       $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';");
       
   163       if ( !$q )
       
   164         $db->_die();
       
   165       if ( $db->numrows() < 1 ) {
       
   166         die_friendly('HTTP authentication error', '<p>Your username and password were valid, but there was a problem getting your user ID.</p>');
       
   167       }
       
   168       $row = $db->fetchrow();
       
   169       $db->free_result();
       
   170       // Quick - lock the account
       
   171       $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']};");
       
   172       if ( !$q )
       
   173         $db->_die();
       
   174       
       
   175       $row['password'] = 'Locked by HTTP auth plugin';
       
   176     }
       
   177     else
       
   178     {
       
   179       // Nope. Just let Enano fail it properly.
       
   180       die_friendly('User does not exist', '<p>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.</p>');
       
   181     }
       
   182   }
       
   183   else
       
   184   {
       
   185     $row = $db->fetchrow();
       
   186     $db->free_result();
       
   187   }
       
   188   
       
   189   $session->register_session($row['user_id'], $user, $row['password'], $level, $remember);
       
   190 }
       
   191 
       
   192 //
       
   193 // ADMIN
       
   194 //
       
   195 
       
   196 $plugins->attachHook('session_started', 'http_auth_session_hook();');
       
   197 
       
   198 if ( getConfig('http_auth_disable_local', 0) == 1 )
       
   199 {
       
   200   $plugins->attachHook('common_post', 'http_auth_tou_hook();');
       
   201 }
       
   202 
       
   203 function http_auth_session_hook()
       
   204 {
       
   205   global $db, $session, $paths, $template, $plugins; // Common objects
       
   206   
       
   207   // Register the admin page
       
   208   $paths->addAdminNode('adm_cat_security', 'HTTP Authentication', 'HTTPAuthConfig');
       
   209   
       
   210   // Disable password change
       
   211   if ( getConfig('http_auth_disable_local', 0) == 1 && $session->user_level < USER_LEVEL_ADMIN )
       
   212   {
       
   213     $link_text = getConfig('http_auth_password_text', false);
       
   214     if ( empty($link_text) )
       
   215       $link_text = false;
       
   216     $link_url = str_replace('%u', $session->username, getConfig('http_auth_password_url', ''));
       
   217     if ( empty($link_url) )
       
   218       $link_url = false;
       
   219     $session->disable_password_change($link_url, $link_text);
       
   220   }
       
   221 }
       
   222 
       
   223 function clean_server_redirect_vars()
       
   224 {
       
   225   foreach ( $_SERVER as $key => $value ) {
       
   226     if ( preg_match($regexp = '/^(REDIRECT_)*/', $key) )
       
   227     {
       
   228       $newkey = preg_replace($regexp, '', $key);
       
   229       if ( !isset($_SERVER[$newkey]) )
       
   230       {
       
   231         $_SERVER[$newkey] = $value;
       
   232       }
       
   233     }
       
   234   }
       
   235 }
       
   236 
       
   237 function http_auth_tou_hook()
       
   238 {
       
   239   global $db, $session, $paths, $template, $plugins; // Common objects
       
   240   
       
   241   // Are we supposed to fail if no authentication information is presented?
       
   242   // first strip REDIRECT_* from $_SERVER variables
       
   243   clean_server_redirect_vars();
       
   244   
       
   245   if ( getConfig('http_auth_mode', 'guest') === 'noguest' && empty($_SERVER['REMOTE_USER']) )
       
   246   {
       
   247     die_friendly('No authentication provided', '<p>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.</p>');
       
   248   }
       
   249   
       
   250   if ( !empty($_SERVER['REMOTE_USER']) && !$session->user_logged_in && !in_array($paths->page, array('Special:Login', 'Special:LoginHTTP', 'Special:Logout')) ) {
       
   251     http_auth_do_login();
       
   252     redirect($paths->page, '', '', 0);
       
   253   }
       
   254   
       
   255   // Are we pending TOU acceptance?
       
   256   if ( $session->user_logged_in && !$session->on_critical_page() && trim(getConfig('register_tou', '')) != '' )
       
   257   {
       
   258     $q = $db->sql_query('SELECT account_active FROM ' . table_prefix . "users WHERE user_id = {$session->user_id};");
       
   259     if ( !$q )
       
   260       $db->_die();
       
   261     
       
   262     list($active) = $db->fetchrow_num();
       
   263     $db->free_result();
       
   264     if ( $active == 1 )
       
   265     {
       
   266       // Pending TOU accept
       
   267       // 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
       
   268       // A bit of a hack, but hey, it works, at least in 1.1.8.
       
   269       // 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
       
   270       // to go the admin CP and re-activate yourself. Good times... erhm, sorry.
       
   271       
       
   272       if ( isset($_POST['tou_agreed']) && $_POST['tou_agreed'] === 'I accept the terms and conditions displayed on this site' )
       
   273       {
       
   274         // Accepted
       
   275         $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 2 WHERE user_id = {$session->user_id};");
       
   276         if ( !$q )
       
   277           $db->_die();
       
   278         
       
   279         return true;
       
   280       }
       
   281       
       
   282       global $output, $lang;
       
   283       $output->set_title('Terms of Use');
       
   284       $output->header();
       
   285       
       
   286       ?>
       
   287       <p>Please read and accept the following terms:</p>
       
   288       
       
   289       <div style="border: 1px solid #000000; height: 300px; width: 60%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: #FFF; margin: 0 auto; padding: 4px;">
       
   290         <?php
       
   291         $terms = getConfig('register_tou', '');
       
   292         echo RenderMan::render($terms);
       
   293         ?>
       
   294       </div>
       
   295       
       
   296       <form method="post">
       
   297         <p style="text-align: center;">
       
   298           <label>
       
   299             <input tabindex="7" type="checkbox" name="tou_agreed" value="I accept the terms and conditions displayed on this site" />
       
   300             <b><?php echo $lang->get('user_reg_lbl_field_tou'); ?></b>
       
   301           </label>
       
   302         </p>
       
   303         <p style="text-align: center;">
       
   304           <input type="submit" value="Continue" />
       
   305         </p>
       
   306       </form>
       
   307       
       
   308       <?php
       
   309       
       
   310       $output->footer();
       
   311       
       
   312       $db->close();
       
   313       exit;
       
   314     }
       
   315   }
       
   316 }
       
   317 
       
   318 function page_Admin_HTTPAuthConfig()
       
   319 {
       
   320   // Security check
       
   321   global $db, $session, $paths, $template, $plugins; // Common objects
       
   322   if ( $session->auth_level < USER_LEVEL_ADMIN )
       
   323     return false;
       
   324   
       
   325   if ( isset($_POST['submit']) )
       
   326   {
       
   327     setConfig('http_auth_enable', isset($_POST['http_auth_enable']) ? '1' : '0');
       
   328     setConfig('http_auth_disable_local', isset($_POST['http_auth_disable_local']) ? '1' : '0');
       
   329     setConfig('http_auth_mode', isset($_POST['http_auth_mode']) && in_array($_POST['http_auth_mode'], array('guest', 'noguest')) ? $_POST['http_auth_mode'] : 'guest');
       
   330     setConfig('http_auth_password_text', $_POST['http_auth_password_text']);
       
   331     setConfig('http_auth_password_url', $_POST['http_auth_password_url']);
       
   332     setConfig('http_auth_email_domain', $_POST['http_auth_email_domain']);
       
   333     
       
   334     echo '<div class="info-box">Your changes have been saved.</div>';
       
   335   }
       
   336   
       
   337   acp_start_form();
       
   338   ?>
       
   339   <div class="tblholder">
       
   340     <table border="0" cellspacing="1" cellpadding="4">
       
   341       <tr>
       
   342         <th colspan="2">
       
   343           HTTP Authentication Configuration
       
   344         </th>
       
   345       </tr>
       
   346       
       
   347       <!-- HTTP enable -->
       
   348       
       
   349       <tr>
       
   350         <td class="row2" style="width: 50%;">
       
   351           Enable HTTP authentication:
       
   352         </td>
       
   353         <td class="row1" style="width: 50%;">
       
   354           <label>
       
   355             <input type="checkbox" name="http_auth_enable" <?php if ( getConfig('http_auth_enable', 0) ) echo 'checked="checked" '; ?>/>
       
   356             Enabled
       
   357           </label>
       
   358         </td>
       
   359       </tr>
       
   360       
       
   361       <!-- Block local auth -->
       
   362       
       
   363       <tr>
       
   364         <td class="row2">
       
   365           Enforce HTTP for single-sign-on:<br />
       
   366           <small>Use this option to force HTTP passwords and accounts to be used, regardless of local accounts, except for administrators.</small>
       
   367         </td>
       
   368         <td class="row1">
       
   369           <label>
       
   370             <input type="checkbox" name="http_auth_disable_local" <?php if ( getConfig('http_auth_disable_local', 0) ) echo 'checked="checked" '; ?>/>
       
   371             Enabled
       
   372           </label>
       
   373         </td>
       
   374       </tr>
       
   375       
       
   376       <!-- Auth mode -->
       
   377       
       
   378       <tr>
       
   379         <td class="row2" rowspan="2">
       
   380           Guest access mode:<br />
       
   381           <small>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.</small>
       
   382         </td>
       
   383         <td class="row1">
       
   384           <label>
       
   385             <input type="radio" name="http_auth_mode" value="guest" <?php if ( getConfig('http_auth_mode', 'guest') === 'guest' ) echo 'checked="checked" '; ?>/>
       
   386             Guests allowed
       
   387           </label>
       
   388           
       
   389           <label>
       
   390             <input type="radio" name="http_auth_mode" value="noguest" <?php if ( getConfig('http_auth_mode', 'guest') === 'noguest' ) echo 'checked="checked" '; ?>/>
       
   391             Fail without authentication
       
   392           </label>
       
   393         </td>
       
   394       </tr>
       
   395       
       
   396       <tr>
       
   397         <td class="row3">
       
   398           Login page URL:
       
   399             <input size="45" type="text" readonly="readonly" value="<?php echo htmlspecialchars(preg_replace('/[?&]auth=[a-f0-9]+/', '', makeUrlComplete('Special', 'LoginHTTP'))); ?>" />
       
   400           
       
   401           <br />
       
   402           <small>Set this URL to require authentication in your webserver's configuration.</small>
       
   403         </td>
       
   404       </tr>
       
   405       
       
   406       <!-- E-mail domain -->
       
   407       
       
   408       <tr>
       
   409         <td class="row2">
       
   410           E-mail address domain for autoregistered users:<br />
       
   411           <small>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
       
   412                  (ideally) reach the user.</small>
       
   413         </td>
       
   414         <td class="row1">
       
   415           <input type="text" name="http_auth_email_domain" value="<?php echo htmlspecialchars(getConfig('http_auth_email_domain', '')); ?>" size="30" />
       
   416         </td>
       
   417       </tr>
       
   418       
       
   419       <!-- Site password change link -->
       
   420       
       
   421       <tr>
       
   422         <td class="row2">
       
   423           External password management link:<br />
       
   424           <small>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.</small>
       
   425         </td>
       
   426         <td class="row1">
       
   427           Link text: <input type="text" name="http_auth_password_text" value="<?php echo htmlspecialchars(getConfig('http_auth_password_text', '')); ?>" size="30" /><br />
       
   428           Link URL:  <input type="text" name="http_auth_password_url" value="<?php echo htmlspecialchars(getConfig('http_auth_password_url', '')); ?>" size="30" />
       
   429         </td>
       
   430       </tr>
       
   431       
       
   432       <tr>
       
   433         <th class="subhead" colspan="2">
       
   434           <input type="submit" name="submit" value="Save changes" />
       
   435         </th>
       
   436       </tr>
       
   437     </table>
       
   438   </div>
       
   439   <?php
       
   440   echo '</form>';
       
   441 }