/[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.21.2.14, Thu May 8 10:12:18 2008 UTC revision 1.21.2.15, Mon May 19 02:55:50 2008 UTC
# Line 1  Line 1 
1  <?php  <?php
2  // $Id $  // $Id$
3    
4    
5  /**  /**
6   * @file   * @file
7   * Enables "Single Sign-Ons" between related Drupal sites on one server   * Enables "Single Sign-Ons" between related Drupal sites on one server
8   * 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.21.2.13 $  
9   */   */
10    
11  // {{{ core functions  /**
12     * @defgroup singlesignon_core Core functions.
13     * @{
14     */
15    
16  include_once('sessions_extra.inc');  include_once('sessions_extra.inc');
17    
# Line 83  function singlesignon_init() { Line 41  function singlesignon_init() {
41    }    }
42    $_singlesignon_bot_matches = variable_get('singlesignon_bot_matches', $variable_defaults);    $_singlesignon_bot_matches = variable_get('singlesignon_bot_matches', $variable_defaults);
43    
44    if (    /**
45      // If the Master URL isn't set, we can't know what to do, so do nothing     * If the Master URL isn't set, we can't know what to do, so do nothing
46      (!$master_url)     * Likewise, bots don't sign on
47      // Likewise, bots don't sign on     */
48      || _singlesignon_is_bot()    if (!$master_url || _singlesignon_is_bot()) {
49    ) {      return;
     return null;  
50    }    }
51    
52    $extra_base_url = _singlesignon_base_url();    $extra_base_url = _singlesignon_base_url();
# Line 101  function singlesignon_init() { Line 58  function singlesignon_init() {
58          drupal_set_message(t('Cookies are required.'), 'error');          drupal_set_message(t('Cookies are required.'), 'error');
59          return;          return;
60        }        }
   
61        // 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
62        // 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.
63        // 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_init() { Line 70  function singlesignon_init() {
70    // arg() only available if bootstrap has reached PATH.    // arg() only available if bootstrap has reached PATH.
71    drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);    drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
72    
73    $arg0 = arg(0);    switch (arg(0)) {
   
   switch ($arg0) {  
74      case 'logout':      case 'logout':
75        if ($user->uid) {        if ($user->uid) {
76          _singlesignon_session_logout($user->uid);          _singlesignon_session_logout($user->uid);
# Line 125  function singlesignon_init() { Line 79  function singlesignon_init() {
79    
80      case 'singlesignon':      case 'singlesignon':
81        if ($extra_base_url == $master_url) {        if ($extra_base_url == $master_url) {
82          _singlesignon_master($master_url, $arg0, arg(1));          _singlesignon_master($master_url, arg(0), arg(1));
83        }        }
84        return;        return;
85    
# Line 136  function singlesignon_init() { Line 90  function singlesignon_init() {
90          // checking yet because the login process happens after this module          // checking yet because the login process happens after this module
91          // called.  Set a flag telling us to do the master/slave checking          // called.  Set a flag telling us to do the master/slave checking
92          // once the login process is done.          // once the login process is done.
93          $_SESSION['singlesignon_just_loggged_in'] = true;          $_SESSION['singlesignon_just_loggged_in'] = TRUE;
94          return;          return;
95        }        }
96    }    }
# Line 167  function _singlesignon_master($master_ur Line 121  function _singlesignon_master($master_ur
121    global $user;    global $user;
122    
123    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'])) {
124      echo t('Thank you for hacking.');      echo 'Invalid request.';
125      exit;      exit;
126    }    }
127    
# Line 197  function _singlesignon_master($master_ur Line 151  function _singlesignon_master($master_ur
151    }    }
152  }  }
153    
154    /**
155     * @} End of "defgroup singlesignon_core".
156     */
157    
158  // }}}  /**
159  // {{{ helper functions   * @defgroup singlesignon_helpers Helper functions.
160     * @{
161     */
162    
163  /**  /**
164   * Sets up the URL and goes to it   * Sets up the URL and goes to it
165   */   */
166  function _singlesignon_goto_url($master_url, $url) {  function _singlesignon_goto_url($master_url, $url) {
167        // url() only available if bootstrap has reached FULL.    // url() only available if bootstrap has reached FULL.
168        drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
169    
170        $query = 'slave_session='. session_id() .'&singlesignon_dest='. _singlesignon_get_dest();    $query = 'slave_session='. session_id() .'&singlesignon_dest='. _singlesignon_get_dest();
171        _singlesignon_goto($master_url . url($url, $query));    _singlesignon_goto($master_url . url($url, $query));
172  }  }
173    
174  /**  /**
175   * Gets the base url and fixess it up a bit   * Gets the base url and fixess it up a bit
176   */   */
177  function _singlesignon_base_url() {  function _singlesignon_base_url() {
178    global $base_url;    return preg_replace('@^(https?://[^/]+).*@', '\\1', $GLOBALS['base_url']);
   
   $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $base_url);  
   return ($scheme_authority);  
179  }  }
180    
181    
# Line 227  function _singlesignon_base_url() { Line 183  function _singlesignon_base_url() {
183   * Combines $base_url and request_uri() in a safe, portable way.   * Combines $base_url and request_uri() in a safe, portable way.
184   */   */
185  function _singlesignon_get_dest() {  function _singlesignon_get_dest() {
186    global $base_url;    $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $GLOBALS['base_url']);
   
   $scheme_authority = preg_replace('@^(https?://[^/]+).*@', '\\1', $base_url);  
187    return rawurlencode($scheme_authority . request_uri());    return rawurlencode($scheme_authority . request_uri());
188  }  }
189    
# Line 252  function _singlesignon_goto($uri) { Line 206  function _singlesignon_goto($uri) {
206  function _singlesignon_validate_sid($sid) {  function _singlesignon_validate_sid($sid) {
207    if (is_array($sid)) {    if (is_array($sid)) {
208      if (count($sid) > 100) {      if (count($sid) > 100) {
209        return false;        return FALSE;
210      }      }
211      foreach ($sid as $value) {      foreach ($sid as $value) {
212        if (!ereg('^[A-Za-z0-9]{1,100}$', $value)) {        if (!ereg('^[A-Za-z0-9]{1,100}$', $value)) {
213          return false;          return FALSE;
214        }        }
215      }      }
216    }    }
217    else {    else {
218      if (!ereg('^[A-Za-z0-9]{1,100}$', $sid)) {      if (!ereg('^[A-Za-z0-9]{1,100}$', $sid)) {
219        return false;        return FALSE;
220      }      }
221    }    }
222    return true;    return TRUE;
223  }  }
224    
225  /**  /**
# Line 290  function _singlesignon_get_default_domai Line 244  function _singlesignon_get_default_domai
244    return $domain['scheme'] .'://'. $domain['subdomain'];    return $domain['scheme'] .'://'. $domain['subdomain'];
245  }  }
246    
247  // }}}  /**
248  // {{{ other hook functions   * @} End of "defgroup singlesignon_helpers".
249     */
250    
251  /**  /**
252   * Implementation of hook_menu().   * Implementation of hook_menu().
253   */   */
254  function singlesignon_menu($maycache) {  function singlesignon_menu($may_cache) {
255    $items = array(    $items = array();
256      array(    if ($may_cache) {
257        $items[] = array(
258        'path' => 'admin/settings/singlesignon',        'path' => 'admin/settings/singlesignon',
259        'title' => t('Shared Sign-on'),        'title' => t('Shared Sign-on'),
260        'description' => t('Shares users and sign-ons between sites (previously called "Single Sign-on"'),        'description' => t('Shares users and sign-ons between sites (previously called "Single Sign-on"'),
261        'callback' => 'drupal_get_form',        'callback' => 'drupal_get_form',
262        'callback arguments' => array('singlesignon_admin_settings'),        'callback arguments' => array('singlesignon_admin_settings'),
263        'access' => user_access('access administration pages'),        'access' => user_access('access administration pages'),
264        'type' => MENU_NORMAL_ITEM,      );
265      ),    }
   );  
   
266    return $items;    return $items;
267  }  }
268    
# Line 316  function singlesignon_menu($maycache) { Line 270  function singlesignon_menu($maycache) {
270   * Provides user interface necessary to administer this module's settings.   * Provides user interface necessary to administer this module's settings.
271   */   */
272  function singlesignon_admin_settings() {  function singlesignon_admin_settings() {
   if (!user_access('access administration pages')) {  
     return drupal_access_denied();  
   }  
   
273    $form = array();    $form = array();
274    $use_domain = variable_get('singlesignon_use_domain_module', 0);    $use_domain = variable_get('singlesignon_use_domain_module', 0);
275    if (module_exists('domain')) {    if (module_exists('domain')) {
# Line 347  function singlesignon_admin_settings() { Line 297  function singlesignon_admin_settings() {
297      '#collapsible' => TRUE,      '#collapsible' => TRUE,
298      '#collapsed' => TRUE,      '#collapsed' => TRUE,
299      '#tree' => TRUE,      '#tree' => TRUE,
300      '#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).  
301  The data below will hopefully help the single sign-on module to  The data below will hopefully help the single sign-on module to
302  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  
303    );    );
304    $form['singlesignon_bot_matches']['useragents_case'] = array(    $form['singlesignon_bot_matches']['useragents_case'] = array(
305      '#type' => 'textarea',      '#type' => 'textarea',
306      '#title' => 'Case-sensitive User Agents',      '#title' => t('Case-sensitive User Agents'),
307      '#rows' => 5,      '#rows' => 5,
308      '#cols' => 40,      '#cols' => 40,
309      '#default_value' => _singlesignon_get_bm_variable('useragents_case'),      '#default_value' => _singlesignon_get_bm_variable('useragents_case'),
# Line 363  EOT Line 311  EOT
311    );    );
312    $form['singlesignon_bot_matches']['useragents_nocase'] = array(    $form['singlesignon_bot_matches']['useragents_nocase'] = array(
313      '#type' => 'textarea',      '#type' => 'textarea',
314      '#title' => 'Case-insensitive User Agents',      '#title' => t('Case-insensitive User Agents'),
315      '#rows' => 5,      '#rows' => 5,
316      '#cols' => 40,      '#cols' => 40,
317      '#default_value' => _singlesignon_get_bm_variable('useragents_nocase'),      '#default_value' => _singlesignon_get_bm_variable('useragents_nocase'),
# Line 371  EOT Line 319  EOT
319    );    );
320    $form['singlesignon_bot_matches']['client_IP'] = array(    $form['singlesignon_bot_matches']['client_IP'] = array(
321      '#type' => 'textarea',      '#type' => 'textarea',
322      '#title' => 'Client IP',      '#title' => t('Client IP'),
323      '#rows' => 5,      '#rows' => 5,
324      '#cols' => 40,      '#cols' => 40,
325      '#default_value' => _singlesignon_get_bm_variable('client_IP'),      '#default_value' => _singlesignon_get_bm_variable('client_IP'),
# Line 379  EOT Line 327  EOT
327    );    );
328    $form['singlesignon_bot_matches']['target_url'] = array(    $form['singlesignon_bot_matches']['target_url'] = array(
329      '#type' => 'textarea',      '#type' => 'textarea',
330      '#title' => 'Target URL',      '#title' => t('Target URL'),
331      '#rows' => 5,      '#rows' => 5,
332      '#cols' => 40,      '#cols' => 40,
333      '#default_value' => _singlesignon_get_bm_variable('target_url'),      '#default_value' => _singlesignon_get_bm_variable('target_url'),
334      '#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)'),
335    );    );
   
336    return system_settings_form($form);    return system_settings_form($form);
337  }  }
338    
339  /**  /**
340   * 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.
341   *   *
342   * @param $variable: The short name of the singlesignon bot matching variable   * @param $variable
343   * @param $text: The default text for the variable   *   The short name of the singlesignon bot matching variable
344     * @param $text
345     *   The default text for the variable
346   */   */
347  function _singlesignon_get_bm_variable($variable) {  function _singlesignon_get_bm_variable($variable) {
   global $_singlesignon_bot_matches;  
   
348    return (preg_replace(    return (preg_replace(
349        array("/^\/(.*?)\/i?$/", "/\|/"),        array("/^\/(.*?)\/i?$/", "/\|/"),
350        array("$1", "\n"),        array("$1", "\n"),
351        $_singlesignon_bot_matches[$variable]        $GLOBALS['_singlesignon_bot_matches'][$variable]
352      ));      ));
353  }  }
354    
# Line 409  function _singlesignon_get_bm_variable($ Line 356  function _singlesignon_get_bm_variable($
356   * Hook for validating a form; verifies the values for singlesignon bot recognition.   * Hook for validating a form; verifies the values for singlesignon bot recognition.
357   */   */
358  function singlesignon_admin_settings_validate($form_id, $form_values, $form) {  function singlesignon_admin_settings_validate($form_id, $form_values, $form) {
359      $s = array();
360    $s['useragents_case']   = _singlesignon_verify_value($form_values, 'useragents_case');    $s['useragents_case']   = _singlesignon_verify_value($form_values, 'useragents_case');
361    $s['useragents_nocase'] = _singlesignon_verify_value($form_values, 'useragents_nocase', '', 'i');    $s['useragents_nocase'] = _singlesignon_verify_value($form_values, 'useragents_nocase', '', 'i');
362    $s['client_IP']        = _singlesignon_verify_value($form_values, 'client_IP', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}');    $s['client_IP']         = _singlesignon_verify_value($form_values, 'client_IP', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}');
363    $s['target_url']       = _singlesignon_verify_value($form_values, 'target_url', '\\\\\/[A-Za-z0-9_\.\*\/\\\\-]*\$$');    $s['target_url']        = _singlesignon_verify_value($form_values, 'target_url', '\\\\\/[A-Za-z0-9_\.\*\/\\\\-]*\$$');
   
364    form_set_value($form['singlesignon_bot_matches'], $s);    form_set_value($form['singlesignon_bot_matches'], $s);
365  }  }
366    
367  /**  /**
368   * Internal function: Verifies one singlesignon bot recognition value.   * Internal function: Verifies one singlesignon bot recognition value.
369   *   *
370   * @param $form_values: The values we're validating   * @param $form_values
371   * @param $value: The name of the value we're validation   *   The values we're validating.
372   * @param $allowed: A regex specifying what values are allowed   * @param $value
373   * @param $extras: The regex parameters (ie. 'i' is case insensitive)   *   The name of the value we're validation.
374     * @param $allowed
375     *   A regex specifying what values are allowed.
376     * @param $extras
377     *   The regex parameters (ie. 'i' is case insensitive).
378   */   */
379  function _singlesignon_verify_value($form_values, $value, $allowed = '', $extras = '') {  function _singlesignon_verify_value($form_values, $value, $allowed = '', $extras = '') {
380    if ($allowed == '') {    if ($allowed == '') {
# Line 437  function _singlesignon_verify_value($for Line 388  function _singlesignon_verify_value($for
388          $rvals[] = $val;          $rvals[] = $val;
389        }        }
390        else {        else {
391          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)));
392        }        }
393      }      }
394      return ('/'. join('|', $rvals) ."/$extras");      return ('/'. join('|', $rvals) ."/$extras");
# Line 445  function _singlesignon_verify_value($for Line 396  function _singlesignon_verify_value($for
396    return ($form_values['singlesignon_bot_matches'][$value]);    return ($form_values['singlesignon_bot_matches'][$value]);
397  }  }
398    
 // }}}  
399    

Legend:
Removed from v.1.21.2.14  
changed lines
  Added in v.1.21.2.15

  ViewVC Help
Powered by ViewVC 1.1.2