From 5097912fd97d7117680f71c9d36af4457efc354b Mon Sep 17 00:00:00 2001
From: Scott Shambarger <sshambar@2956739.no-reply.drupal.org>
Date: Tue, 22 Jul 2014 11:17:19 +0200
Subject: [PATCH] Validate session and authname are in sync.

---
 webserver_auth.module | 139 +++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 103 insertions(+), 36 deletions(-)

diff --git a/webserver_auth.module b/webserver_auth.module
index 4c9b423..5801250 100644
--- a/webserver_auth.module
+++ b/webserver_auth.module
@@ -72,47 +72,108 @@ function webserver_auth_boot() {
 }
 
 /**
- * Helper function, does heavy lifting of webserver_auth login and logout.
+ * Logout the current user (ending session)
+ *
+ * Ends current session mirroring user_logout() (without the redirect)
+ */
+function _webserver_auth_logout() {
+
+  global $user;
+
+  // logout current user (sadly, user module doesn't have an api for this)
+  watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
+
+  module_invoke_all('user_logout', $user);
+
+  // Destroy the current session, and reset $user to the anonymous user.
+  session_destroy();
+}
+
+/**
+ * Validates that the authname from the environment and the current session
+ * are in sync, and if not ends the current session and attempts a fresh
+ * login for the remote user.
  */
 function webserver_auth_route() {
-  global $user, $language;
-   
-  // First we simply check if the user has been logged in by Drupal already.  
-  // If they have, we're done.
-  if (isset($user->uid) && ($user->uid != 0)) {
-    return;
-  }
-  // Get the login name from the $_SERVER variable.
+
+  /* Get authname from environment */
   $authname = webserver_auth_retrieve_remote_user();
- 
+
   if ($authname) {
     drupal_alter('webserver_auth_authname', $authname);
-      
-    if (webserver_auth_attempt_login($authname)) {
+  }
+
+  if (! $authname) {
+    // Empty authname is anonymous: close any current session.
+    if (user_is_logged_in()) {
+      _webserver_auth_logout();
+    }
+
+    return;
+  }
+
+  /* Check if the authname matches the current session.
+     If it does, we're done. */
+  if (user_is_logged_in()) {
+
+    /* Validate session authname.  We don't match against uid as authmap may
+       link to a user with uid different than authname. */
+    if (isset($_SESSION['webserver_authname'])
+	&& ($_SESSION['webserver_authname'] == $authname)) {
       return;
     }
-    else {
-      // We didn't find the user so we create an account for them if set to do 
-      // so. Note, hook_user_presave() is called during this process. 
-      // That's where we modify the user account to add email etc.
-      if (variable_get('webserver_auth_create_user', FALSE)) {
-        try {
-          user_external_login_register($authname, 'webserver_auth');
-        }
-        catch(PDOException $e) {
-          watchdog('webserver_auth', 'unable to create new user record for user: ' . $authname . '.  This is probably because they already exist in the user table but not in the authmap table for webserver_auth.');
-          return;
-        }
-      }
+
+    // We need to logout the current user
+    _webserver_auth_logout();
+  }
+
+  if (webserver_auth_attempt_login($authname)) {
+    return;
+  }
+
+  // Check if we should create accounts for unknown users
+  if (! variable_get('webserver_auth_create_user', FALSE)) {
+    watchdog('webserver_auth', 'user @authname does not exist in the database or is not included in the authmap table for webserver_auth, and the create user option is disabled.', array('@authname' => $authname));
+    return;
+  }
+
+  // First check if just authmap is missing
+  $account = user_load_by_name($authname);
+  if ($account) {
+
+    $authmaps = user_get_authmaps($authname);
+    if (is_array($authmaps) && isset($authmaps['webserver_auth'])) {
+      watchdog('webserver_auth', 'user @authname blocked.', array('@authname' => $authname), WATCHDOG_INFO);
+      return;
     }
-   
-    // Still here?
-    watchdog('webserver_auth', 'user: @authname does not exist in the database or is not included in the authmap table for webserver_auth and cannot login.', array('@authname' => $authname));
+
+    /* match_existing must be TRUE initially so admin users aren't logged out
+       when they enable the module */
+    if (! variable_get('webserver_auth_match_existing', TRUE)) {
+      watchdog('webserver_auth', 'account exists for user @authname, but matching against existing accounts is disabled.  Therefore you must manually create an entry in the authmap table for webserver_auth.', array('@authname', $authname), WATCHDOG_ERROR);
+      return;
+    }
+
+    // add missing authmap
+    user_set_authmaps($account, array("authname_webserver_auth" => $authname));
+
+    // now try login again
+    if (! _webserver_auth_attempt_login($authname)) {
+      watchdog('webserver_auth', 'user @authname blocked.', array('@authname' => $authname), WATCHDOG_INFO);
+    }
+    return;
   }
-  else {
-    watchdog('webserver_auth', 'No authname');
+
+  /* We didn't find the user so we create an account for them.
+     Note, hook_user_presave() is called during this process.
+     That's where we modify the user account to add email etc. */
+  try {
+    user_external_login_register($authname, 'webserver_auth');
+    $_SESSION['webserver_authname'] = $authname;
+  }
+  catch(PDOException $e) {
+    watchdog('webserver_auth', 'unable to create new user record for user @authname.', array('@authname', $authname), WATCHDOG_ERROR);
   }
-  return;
 }
 
 /**
@@ -170,6 +231,8 @@ function webserver_auth_attempt_login($preparedauthname) {
     // We found the user so just log them in.
     $form_state['uid'] = $uid;
     user_login_submit(array(), $form_state);
+    // Save authname in session to compare in webserver_auth_route()
+    $_SESSION['webserver_authname'] = $preparedauthname;
     return true;
   }
   return false;
@@ -203,10 +266,7 @@ function webserver_auth_user_insert(&$edit, $account, $category) {
   
   // depending on the module setting, add all new users created using any means to the authmap table
   if (variable_get('webserver_auth_add_all_new', FALSE)) {
-	$query = db_merge('authmap')
-	  ->key(array('uid' => $account->uid))
-	  ->fields(array('uid' => $account->uid, 'authname' => $account->name, 'module' => 'webserver_auth'))
-	  ->execute();
+    user_set_authmaps($account, array("authname_webserver_auth" => $account->name));
   }
 }
 
@@ -237,6 +297,13 @@ function webserver_auth_settings() {
     '#maxlength' => 55,
     '#description' => t ('Append this domain name to each new user in order to generate their email address.'),
   );
+  $form['webserver_auth_match_existing'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Match external names to existing Drupal users',
+    '#default_value' => variable_get('webserver_auth_match_existing', TRUE),
+    '#description' => t("Match against the usernames of existing Drupal users that weren't created by this module when validating logins."
+      . "  Disable if you want to manage authentication module mappings manually."),
+  );
   $form['advanced'] = array(
     '#type' => 'fieldset',
     '#title' => t('Advanced settings'),
@@ -325,4 +392,4 @@ function webserver_auth_webserver_auth_authname_alter(&$username) {
     $fields = explode ('@', $username);
     $username = $fields [0];
   }
-}
\ No newline at end of file
+}
-- 
1.8.5.2 (Apple Git-48)

