/[drupal]/contributions/modules/singlesignon/singlesignon.module
ViewVC logotype

Diff of /contributions/modules/singlesignon/singlesignon.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.32, Thu May 8 10:13:18 2008 UTC revision 1.33, Mon May 19 03:08:12 2008 UTC
# Line 1  Line 1 
1  <?php  <?php
2  // $Id: singlesignon.module,v 1.31 2008/05/05 13:38:20 wayland76 Exp $  // $Id$
3    
   
4  /**  /**
5   * @file   * @file
6   * Enables "Single Sign-Ons" between related Drupal sites on one server   * Enables "Single Sign-Ons" between related Drupal sites on one server
7   * with a shared database.   * with a shared database.
  *  
  * Installation and activation are simple:  
  *   + Put this file in your modules directory.  
  *   + If you haven't done so already, create site sub-directories in the  
  *     "sites" directory for each of your domains.  Put "settings.php"  
  *     files in each of those directories.  Edit each of the settings.php  
  *     files to values appropriate for the given domain.  
  *   + Make sure the "$db_prefix" variable is set correctly in each  
  *     settings.php file (see details below).  
  *   + Create the site specific and shared database tables defined in the  
  *     "$db_prefix" variable (see details below).  
  *   + On each site, starting with the master site:  
  *      + Log in as administrator.  
  *      + Enable the "singlesignon" module  
  *         (via the checkbox in the administer | modules interface).  
  *      + Enter the URL of the master site  
  *         (via administer | settings | singlesignon).  
  *   + Delete cookies from all of your Drupal sites.  
  *  
  * This module relies on several tables being shared between the master and  
  * slave sites.  Here is an example of the "$db_prefix" variable you need to  
  * establish in all of your "settings.php" files in the "sites" directory.  
  * Each site should have a unique name in place of "somesitename_".  While  
  * you can rename "shared_" to something else, the prefixes must be the  
  * same for all sites.  
  *  
  * @verbatim  
  * $db_prefix = array(  
  *   'default'        => 'somesitename_',  
  *   'authmap'        => 'shared_',  
  *   'profile_fields' => 'shared_',  
  *   'profile_values' => 'shared_',  
  *   'role'           => 'shared_',  
  *   'sequences'      => 'shared_',  
  *   'sessions'       => 'shared_',  
  *   'users'          => 'shared_',  
  *   'users_roles'    => 'shared_',  
  *   'users_uid_seq'  => 'shared_',  // for pgsql  
  * );  
  * @endverbatim  
  *  
  * @link     http://drupal.org/project/singlesignon  
  * @author   Primary Author: Daniel Convissor <danielc@analysisandsolutions.com>  
  * @author   Maintainer: Tim Nelson <wayland@wayland.id.au>  
  * @version  $Revision: 1.31 $  
8   */   */
9    
10  // {{{ core functions  /**
11     * @defgroup singlesignon_core Core functions.
12     * @{
13     */
14    
15  include_once('sessions_extra.inc');  include_once('sessions_extra.inc');
16    
# Line 83  function singlesignon_boot() { Line 40  function singlesignon_boot() {
40    }    }
41    $_singlesignon_bot_matches = variable_get('singlesignon_bot_matches', $variable_defaults);    $_singlesignon_bot_matches = variable_get('singlesignon_bot_matches', $variable_defaults);
42    
43    if (    // If no master URL is set or we are serving a bot, do nothing.
44      // If the Master URL isn't set, we can't know what to do, so do nothing    if (!$master_url || _singlesignon_is_bot()) {
45      (!$master_url)      return;
     // Likewise, bots don't sign on  
     || _singlesignon_is_bot()  
   ) {  
     return null;  
46    }    }
47    
48    $extra_base_url = _singlesignon_base_url();    $extra_base_url = _singlesignon_base_url();
# Line 101  function singlesignon_boot() { Line 54  function singlesignon_boot() {
54          drupal_set_message(t('Cookies are required.'), 'error');          drupal_set_message(t('Cookies are required.'), 'error');
55          return;          return;
56        }        }
   
57        // This is the user's first hit to a slave site.  Take note of their        // This is the user's first hit to a slave site.  Take note of their
58        // session ID, since that's how we tell if they've been here or not.        // session ID, since that's how we tell if they've been here or not.
59        // Then go to the master site to see if they are logged in over there.        // Then go to the master site to see if they are logged in over there.
# Line 114  function singlesignon_boot() { Line 66  function singlesignon_boot() {
66    // arg() only available if bootstrap has reached PATH.    // arg() only available if bootstrap has reached PATH.
67    drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);    drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
68    
69    $arg0 = arg(0);    switch (arg(0)) {
   
   switch ($arg0) {  
70      case 'logout':      case 'logout':
71        if ($user->uid) {        if ($user->uid) {
72          _singlesignon_session_logout($user->uid);          _singlesignon_session_logout($user->uid);
# Line 125  function singlesignon_boot() { Line 75  function singlesignon_boot() {
75    
76      case 'singlesignon':      case 'singlesignon':
77        if ($extra_base_url == $master_url) {        if ($extra_base_url == $master_url) {
78          _singlesignon_master($master_url, $arg0, arg(1));          _singlesignon_master($master_url, arg(0), arg(1));
79        }        }
80        return;        return;
81    
# Line 136  function singlesignon_boot() { Line 86  function singlesignon_boot() {
86          // checking yet because the login process happens after this module          // checking yet because the login process happens after this module
87          // called.  Set a flag telling us to do the master/slave checking          // called.  Set a flag telling us to do the master/slave checking
88          // once the login process is done.          // once the login process is done.
89          $_SESSION['singlesignon_just_loggged_in'] = true;          $_SESSION['singlesignon_just_loggged_in'] = TRUE;
90          return;          return;
91        }        }
92    }    }
# Line 167  function _singlesignon_master($master_ur Line 117  function _singlesignon_master($master_ur
117    global $user;    global $user;
118    
119    if (empty($_GET['singlesignon_dest']) || is_array($_GET['singlesignon_dest']) || empty($_GET['slave_session']) || is_array($_GET['slave_session']) || !_singlesignon_validate_sid($_GET['slave_session'])) {    if (empty($_GET['singlesignon_dest']) || is_array($_GET['singlesignon_dest']) || empty($_GET['slave_session']) || is_array($_GET['slave_session']) || !_singlesignon_validate_sid($_GET['slave_session'])) {
120      echo t('Thank you for hacking.');      echo 'Invalid request.';
121      exit;      exit;
122    }    }
123    
# Line 197  function _singlesignon_master($master_ur Line 147  function _singlesignon_master($master_ur
147    }    }
148  }  }
149    
150    /**
151     * @} End of "defgroup singlesignon_core".
152     */
153    
154  // }}}  /**
155  // {{{ helper functions   * @defgroup singlesignon_helpers Helper functions.
156     * @{
157     */
158    
159  /**  /**
160   * Sets up the URL and goes to it   * Sets up the URL and goes to it
161   */   */
162  function _singlesignon_goto_url($master_url, $url) {  function _singlesignon_goto_url($master_url, $url) {
163        // url() only available if bootstrap has reached FULL.    // url() only available if bootstrap has reached FULL.
164        drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
165    
166        $query = 'slave_session='. session_id() .'&singlesignon_dest='. _singlesignon_get_dest();    $query = 'slave_session='. session_id() .'&singlesignon_dest='. _singlesignon_get_dest();
167        _singlesignon_goto($master_url . url($url, array('query' => $query)));    _singlesignon_goto($master_url . url($url, $query));
168  }  }
169    
170  /**  /**
171   * Gets the base url and fixess it up a bit   * Gets the base url and fixess it up a bit
172   */   */
173  function _singlesignon_base_url() {  function _singlesignon_base_url() {
174    global $base_url;    return preg_replace('@^(https?://[^/]+).*@', '\\1', $GLOBALS['base_url']);
   
   $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $base_url);  
   return ($scheme_authority);  
175  }  }
176    
   
177  /**  /**
178   * Combines $base_url and request_uri() in a safe, portable way.   * Combines $base_url and request_uri() in a safe, portable way.
179   */   */
180  function _singlesignon_get_dest() {  function _singlesignon_get_dest() {
181    global $base_url;    $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $GLOBALS['base_url']);
   
   $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $base_url);  
182    return rawurlencode($scheme_authority . request_uri());    return rawurlencode($scheme_authority . request_uri());
183  }  }
184    
# Line 252  function _singlesignon_goto($uri) { Line 201  function _singlesignon_goto($uri) {
201  function _singlesignon_validate_sid($sid) {  function _singlesignon_validate_sid($sid) {
202    if (is_array($sid)) {    if (is_array($sid)) {
203      if (count($sid) > 100) {      if (count($sid) > 100) {
204        return false;        return FALSE;
205      }      }
206      foreach ($sid as $value) {      foreach ($sid as $value) {
207        if (!ereg('^[A-Za-z0-9]{1,100}$', $value)) {        if (!ereg('^[A-Za-z0-9]{1,100}$', $value)) {
208          return false;          return FALSE;
209        }        }
210      }      }
211    }    }
212    else {    else {
213      if (!ereg('^[A-Za-z0-9]{1,100}$', $sid)) {      if (!ereg('^[A-Za-z0-9]{1,100}$', $sid)) {
214        return false;        return FALSE;
215      }      }
216    }    }
217    return true;    return TRUE;
218  }  }
219    
220  /**  /**
# Line 290  function _singlesignon_get_default_domai Line 239  function _singlesignon_get_default_domai
239    return $domain['scheme'] .'://'. $domain['subdomain'];    return $domain['scheme'] .'://'. $domain['subdomain'];
240  }  }
241    
242  // }}}  + /**
243  // {{{ other hook functions  +  * @} End of "defgroup singlesignon_helpers".
244    +  */
245    
246  /**  /**
247   * Implementation of hook_menu().   * Implementation of hook_menu().
# Line 313  function singlesignon_menu() { Line 263  function singlesignon_menu() {
263   * Provides user interface necessary to administer this module's settings.   * Provides user interface necessary to administer this module's settings.
264   */   */
265  function singlesignon_admin_settings() {  function singlesignon_admin_settings() {
   if (!user_access('access administration pages')) {  
     return drupal_access_denied();  
   }  
   
266    $form = array();    $form = array();
267    $use_domain = variable_get('singlesignon_use_domain_module', 0);    $use_domain = variable_get('singlesignon_use_domain_module', 0);
268    if (module_exists('domain')) {    if (module_exists('domain')) {
# Line 344  function singlesignon_admin_settings() { Line 290  function singlesignon_admin_settings() {
290      '#collapsible' => TRUE,      '#collapsible' => TRUE,
291      '#collapsed' => TRUE,      '#collapsed' => TRUE,
292      '#tree' => TRUE,      '#tree' => TRUE,
293      '#description' => <<<EOT      '#description' => t('Single sign-on does not play well with bots (ie. search engines).
 Single sign-on does not play well with bots (ie. search engines).  
294  The data below will hopefully help the single sign-on module to  The data below will hopefully help the single sign-on module to
295  recognise bots and let them through (ie. it plays nicely with the recognised bots).  recognise bots and let them through (ie. it plays nicely with the recognised bots).'),
 EOT  
296    );    );
297    $form['singlesignon_bot_matches']['useragents_case'] = array(    $form['singlesignon_bot_matches']['useragents_case'] = array(
298      '#type' => 'textarea',      '#type' => 'textarea',
299      '#title' => 'Case-sensitive User Agents',      '#title' => t('Case-sensitive User Agents'),
300      '#rows' => 5,      '#rows' => 5,
301      '#cols' => 40,      '#cols' => 40,
302      '#default_value' => _singlesignon_get_bm_variable('useragents_case'),      '#default_value' => _singlesignon_get_bm_variable('useragents_case'),
# Line 360  EOT Line 304  EOT
304    );    );
305    $form['singlesignon_bot_matches']['useragents_nocase'] = array(    $form['singlesignon_bot_matches']['useragents_nocase'] = array(
306      '#type' => 'textarea',      '#type' => 'textarea',
307      '#title' => 'Case-insensitive User Agents',      '#title' => t('Case-insensitive User Agents'),
308      '#rows' => 5,      '#rows' => 5,
309      '#cols' => 40,      '#cols' => 40,
310      '#default_value' => _singlesignon_get_bm_variable('useragents_nocase'),      '#default_value' => _singlesignon_get_bm_variable('useragents_nocase'),
# Line 368  EOT Line 312  EOT
312    );    );
313    $form['singlesignon_bot_matches']['client_IP'] = array(    $form['singlesignon_bot_matches']['client_IP'] = array(
314      '#type' => 'textarea',      '#type' => 'textarea',
315      '#title' => 'Client IP',      '#title' => t('Client IP'),
316      '#rows' => 5,      '#rows' => 5,
317      '#cols' => 40,      '#cols' => 40,
318      '#default_value' => _singlesignon_get_bm_variable('client_IP'),      '#default_value' => _singlesignon_get_bm_variable('client_IP'),
# Line 376  EOT Line 320  EOT
320    );    );
321    $form['singlesignon_bot_matches']['target_url'] = array(    $form['singlesignon_bot_matches']['target_url'] = array(
322      '#type' => 'textarea',      '#type' => 'textarea',
323      '#title' => 'Target URL',      '#title' => t('Target URL'),
324      '#rows' => 5,      '#rows' => 5,
325      '#cols' => 40,      '#cols' => 40,
326      '#default_value' => _singlesignon_get_bm_variable('target_url'),      '#default_value' => _singlesignon_get_bm_variable('target_url'),
327      '#description' => t('A list of case-sensitive strings that might match a referrer.  <b>Not recommended</b> (in general; we have a few specific cases here)'),      '#description' => t('A list of case-sensitive strings that might match a referrer.  <b>Not recommended</b> (in general; we have a few specific cases here)'),
328    );    );
   
329    return system_settings_form($form);    return system_settings_form($form);
330  }  }
331    
332  /**  /**
333   * Internal function for use of singlesignon_admin_settings; turns | separated string into \n separated string.   * Internal function for use of singlesignon_admin_settings; turns | separated string into \n separated string.
334   *   *
335   * @param $variable: The short name of the singlesignon bot matching variable   * @param $variable
336   * @param $text: The default text for the variable   *   The short name of the singlesignon bot matching variable
337     * @param $text
338     *   The default text for the variable
339   */   */
340  function _singlesignon_get_bm_variable($variable) {  function _singlesignon_get_bm_variable($variable) {
   global $_singlesignon_bot_matches;  
   
341    return (preg_replace(    return (preg_replace(
342        array("/^\/(.*?)\/i?$/", "/\|/"),        array("/^\/(.*?)\/i?$/", "/\|/"),
343        array("$1", "\n"),        array("$1", "\n"),
344        $_singlesignon_bot_matches[$variable]        $GLOBALS['_singlesignon_bot_matches'][$variable]
345      ));      ));
346  }  }
347    
# Line 406  function _singlesignon_get_bm_variable($ Line 349  function _singlesignon_get_bm_variable($
349   * Hook for validating a form; verifies the values for singlesignon bot recognition.   * Hook for validating a form; verifies the values for singlesignon bot recognition.
350   */   */
351  function singlesignon_admin_settings_validate($form, &$form_state) {  function singlesignon_admin_settings_validate($form, &$form_state) {
352      $s = array();
353    $s['useragents_case']   = _singlesignon_verify_value($form_state['values'], 'useragents_case');    $s['useragents_case']   = _singlesignon_verify_value($form_state['values'], 'useragents_case');
354    $s['useragents_nocase'] = _singlesignon_verify_value($form_state['values'], 'useragents_nocase', '', 'i');    $s['useragents_nocase'] = _singlesignon_verify_value($form_state['values'], 'useragents_nocase', '', 'i');
355    $s['client_IP']        = _singlesignon_verify_value($form_state['values'], 'client_IP', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}');    $s['client_IP']         = _singlesignon_verify_value($form_state['values'], 'client_IP', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}');
356    $s['target_url']       = _singlesignon_verify_value($form_state['values'], 'target_url', '\\\\\/[A-Za-z0-9_\.\*\/\\\\-]*\$$');    $s['target_url']        = _singlesignon_verify_value($form_state['values'], 'target_url', '\\\\\/[A-Za-z0-9_\.\*\/\\\\-]*\$$');
357    
358    $form_state['values']['singlesignon_bot_matches'] = $s;    $form_state['values']['singlesignon_bot_matches'] = $s;
359  }  }
# Line 417  function singlesignon_admin_settings_val Line 361  function singlesignon_admin_settings_val
361  /**  /**
362   * Internal function: Verifies one singlesignon bot recognition value.   * Internal function: Verifies one singlesignon bot recognition value.
363   *   *
364   * @param $form_values: The values we're validating   * @param $form_values:
365   * @param $value: The name of the value we're validation   *   The values we're validating
366   * @param $allowed: A regex specifying what values are allowed   * @param $value:
367   * @param $extras: The regex parameters (ie. 'i' is case insensitive)   *   The name of the value we're validation
368     * @param $allowed:
369     *   A regex specifying what values are allowed
370     * @param $extras:
371     *   The regex parameters (ie. 'i' is case insensitive)
372   */   */
373  function _singlesignon_verify_value($form_values, $value, $allowed = '', $extras = '') {  function _singlesignon_verify_value($form_values, $value, $allowed = '', $extras = '') {
374    if ($allowed == '') {    if ($allowed == '') {
# Line 434  function _singlesignon_verify_value($for Line 382  function _singlesignon_verify_value($for
382          $rvals[] = $val;          $rvals[] = $val;
383        }        }
384        else {        else {
385          form_set_error('', t("The strings in $value contain non-word characters (we allow $allowed at the moment, and '". $val ."' is a problem)"));          form_set_error('', t("The strings in @value contain non-word characters (we allow @allowed at the moment, and '@wrong-value' is a problem)", array('@value' => $value, '@allowed' => $allowed, '@wrong-value' => $val)));
386        }        }
387      }      }
388      return ('/'. join('|', $rvals) ."/$extras");      return ('/'. join('|', $rvals) ."/$extras");
# Line 442  function _singlesignon_verify_value($for Line 390  function _singlesignon_verify_value($for
390    return ($form_values['singlesignon_bot_matches'][$value]);    return ($form_values['singlesignon_bot_matches'][$value]);
391  }  }
392    
 // }}}  
393    

Legend:
Removed from v.1.32  
changed lines
  Added in v.1.33

  ViewVC Help
Powered by ViewVC 1.1.2