| 1 |
<?php
|
| 2 |
// $Id$
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @defgroup session_limit Limits multiple sessions per user.
|
| 6 |
*
|
| 7 |
* Limits multiple sessions per user.
|
| 8 |
*/
|
| 9 |
/**
|
| 10 |
* @file
|
| 11 |
*
|
| 12 |
* Main module file for session_limit
|
| 13 |
*
|
| 14 |
* @ingroup session_limit
|
| 15 |
*/
|
| 16 |
|
| 17 |
|
| 18 |
/**
|
| 19 |
* Established Sessions do NOT need to verify every page load.
|
| 20 |
* new Session must deal w/ determining which connection is cut.
|
| 21 |
*/
|
| 22 |
|
| 23 |
/**
|
| 24 |
* Implementation of hook_settings().
|
| 25 |
*/
|
| 26 |
function session_limit_settings() {
|
| 27 |
$form['session_limit_max'] = array(
|
| 28 |
'#type' => 'textfield',
|
| 29 |
'#title' => t('Maximum number of active sessions'),
|
| 30 |
'#default_value' => variable_get('session_limit_max', 1),
|
| 31 |
'#size' => 2,
|
| 32 |
'#maxlength' => 3,
|
| 33 |
'#description' => t('The maximum number of active sessions a user can have. 0 implies unlimited sessions.')
|
| 34 |
);
|
| 35 |
$form['session_limit_auto_drop'] = array(
|
| 36 |
'#type' => 'checkbox',
|
| 37 |
'#title' => t('Automatically drop the oldest session without prompting.'),
|
| 38 |
'#default_value' => variable_get('session_limit_auto_drop', 0)
|
| 39 |
);
|
| 40 |
if (module_exists('masquerade')) {
|
| 41 |
$form['session_limit_masquerade_ignore'] = array(
|
| 42 |
'#type' => 'checkbox',
|
| 43 |
'#title' => t('Ignore masqueraded sessions.'),
|
| 44 |
'#description' => t("When a user administrator uses the masquerade module to impersonate a different user, it won't count against the session limit counter"),
|
| 45 |
'#default_value' => variable_get('session_limit_masquerade_ignore', FALSE)
|
| 46 |
);
|
| 47 |
}
|
| 48 |
$form['submit'] = array(
|
| 49 |
'#type' => 'submit',
|
| 50 |
'#value' => t('Save configuration')
|
| 51 |
);
|
| 52 |
|
| 53 |
return $form;
|
| 54 |
}
|
| 55 |
|
| 56 |
function session_limit_settings_validate($form, &$form_state) {
|
| 57 |
$maxsessions = $form_state['values']['session_limit_max'];
|
| 58 |
if (!is_numeric($maxsessions)) {
|
| 59 |
form_set_error('session_limit_max', t('You must enter a number for the maximum number of active sessions'));
|
| 60 |
}
|
| 61 |
elseif ($maxsessions < 0) {
|
| 62 |
form_set_error('session_limit_max', t('Maximum number of active sessions must be positive'));
|
| 63 |
}
|
| 64 |
}
|
| 65 |
|
| 66 |
function session_limit_settings_submit($form, &$form_state) {
|
| 67 |
variable_set('session_limit_max', $form_state['values']['session_limit_max']);
|
| 68 |
variable_set('session_limit_auto_drop', $form_state['values']['session_limit_auto_drop']);
|
| 69 |
variable_set('session_limit_masquerade_ignore', $form_state['values']['session_limit_masquerade_ignore']);
|
| 70 |
drupal_set_message(t('Settings saved.'));
|
| 71 |
}
|
| 72 |
|
| 73 |
/**
|
| 74 |
* Implementation of hook_help().
|
| 75 |
*/
|
| 76 |
function session_limit_help($path, $args) {
|
| 77 |
switch ($path) {
|
| 78 |
case 'session/limit':
|
| 79 |
return t('The maximum number of simultaneous sessions (@num) for your account has been reached. You did not log off from a previous session or someone else is logged on to your account. This may indicate that your account has been compromised or that account sharing is limited on this site. Please contact the site administrator if you suspect your account has been compromised.', array('@num' => variable_get('session_limit_max', 1)));
|
| 80 |
}
|
| 81 |
}
|
| 82 |
|
| 83 |
/**
|
| 84 |
* Implementation of hook_init().
|
| 85 |
*
|
| 86 |
* Determine whether session has been verified.
|
| 87 |
* Redirect user if over session limit.
|
| 88 |
*/
|
| 89 |
function session_limit_init() {
|
| 90 |
global $user;
|
| 91 |
if (variable_get('session_limit_max', 1) && ($user->uid > 1) && !isset($_SESSION['session_limit'])) {
|
| 92 |
|
| 93 |
// Exclude from the redirect.
|
| 94 |
if ((arg(0) == 'session' && arg(1) == 'limit') || arg(0) == 'logout') {
|
| 95 |
return;
|
| 96 |
}
|
| 97 |
|
| 98 |
if (module_exists('masquerade') && variable_get('session_limit_masquerade_ignore', FALSE)) {
|
| 99 |
$result = db_query('SELECT COUNT(s.uid) FROM {sessions} AS s
|
| 100 |
LEFT JOIN {masquerade} AS m ON s.uid = m.uid_as AND s.sid = m.sid
|
| 101 |
WHERE s.uid = %d AND m.sid IS NULL', $user->uid);
|
| 102 |
}
|
| 103 |
else {
|
| 104 |
$result = db_query('SELECT COUNT(*) FROM {sessions} WHERE uid = %d', $user->uid);
|
| 105 |
}
|
| 106 |
|
| 107 |
if (db_result($result) > variable_get('session_limit_max', 1)) {
|
| 108 |
// redirect to session handler.
|
| 109 |
drupal_goto('session/limit');
|
| 110 |
}
|
| 111 |
else {
|
| 112 |
// mark session as verified to bypass this in future.
|
| 113 |
$_SESSION['session_limit'] = TRUE;
|
| 114 |
}
|
| 115 |
}
|
| 116 |
}
|
| 117 |
|
| 118 |
/**
|
| 119 |
* Implementation of hook_menu().
|
| 120 |
*/
|
| 121 |
function session_limit_menu() {
|
| 122 |
$items['session/limit'] = array(
|
| 123 |
'title' => 'Session limit exceeded',
|
| 124 |
'page callback' => 'drupal_get_form',
|
| 125 |
'page arguments' => array('session_limit_page'),
|
| 126 |
'access callback' => 'user_is_logged_in',
|
| 127 |
'type' => MENU_CALLBACK
|
| 128 |
);
|
| 129 |
$items['mysessions'] = array(
|
| 130 |
'title' => 'My sessions',
|
| 131 |
'page callback' => 'drupal_get_form',
|
| 132 |
'page arguments' => array('session_limit_page'),
|
| 133 |
'access callback' => 'user_is_logged_in',
|
| 134 |
'type' => MENU_SUGGESTED_ITEM
|
| 135 |
);
|
| 136 |
$items['admin/settings/session_limit'] = array(
|
| 137 |
'title' => 'Session Limit',
|
| 138 |
'description' => 'Configure session limits.',
|
| 139 |
'page callback' => 'drupal_get_form',
|
| 140 |
'page arguments' => array('session_limit_settings'),
|
| 141 |
'access callback' => 'user_access',
|
| 142 |
'access arguments' => array('administer site configuration'),
|
| 143 |
'type' => MENU_NORMAL_ITEM
|
| 144 |
);
|
| 145 |
return $items;
|
| 146 |
}
|
| 147 |
|
| 148 |
|
| 149 |
/**
|
| 150 |
* Display/Delete sessions..
|
| 151 |
*/
|
| 152 |
function session_limit_page() {
|
| 153 |
global $user;
|
| 154 |
|
| 155 |
if (!$user->uid > 0) {
|
| 156 |
drupal_goto();
|
| 157 |
}
|
| 158 |
|
| 159 |
if (variable_get('session_limit_auto_drop', 0)) {
|
| 160 |
// Get the oldest session.
|
| 161 |
$sid = db_result(db_query_range("SELECT sid FROM {sessions} WHERE uid = %d ORDER BY timestamp", $user->uid, 0, 1));
|
| 162 |
|
| 163 |
if ($sid) {
|
| 164 |
_session_limit_disconnect($sid);
|
| 165 |
}
|
| 166 |
|
| 167 |
drupal_goto();
|
| 168 |
}
|
| 169 |
|
| 170 |
$result = db_query('SELECT * FROM {sessions} WHERE uid = %d', $user->uid);
|
| 171 |
while ($obj = db_fetch_object($result)) {
|
| 172 |
if ($user->sid == $obj->sid) {
|
| 173 |
$message = t('Your current session.');
|
| 174 |
}
|
| 175 |
else {
|
| 176 |
unset($message);
|
| 177 |
}
|
| 178 |
|
| 179 |
$sids[$obj->sid] = t('<strong>Host:</strong> %host (idle: %time) <b>@message</b>',
|
| 180 |
array(
|
| 181 |
'%host' => $obj->hostname,
|
| 182 |
'@message' => $message,
|
| 183 |
'%time' => format_interval(time() - $obj->timestamp))
|
| 184 |
);
|
| 185 |
}
|
| 186 |
$form['sid'] = array(
|
| 187 |
'#type' => 'radios',
|
| 188 |
'#title' => t('Select a session to disconnect.'),
|
| 189 |
'#options' => $sids,
|
| 190 |
);
|
| 191 |
$form['submit'] = array(
|
| 192 |
'#type' => 'submit',
|
| 193 |
'#value' => t('Disconnect session'),
|
| 194 |
);
|
| 195 |
return $form;
|
| 196 |
}
|
| 197 |
|
| 198 |
/**
|
| 199 |
* Handler for submissions from session_limit_page().
|
| 200 |
*/
|
| 201 |
function session_limit_page_submit($form, &$form_state) {
|
| 202 |
global $user;
|
| 203 |
|
| 204 |
if ($user->sid == $form_state['values']['sid']) {
|
| 205 |
// force a normal logout for ourself.
|
| 206 |
drupal_set_message(t('Your session has been disconnected.'));
|
| 207 |
drupal_goto('logout');
|
| 208 |
}
|
| 209 |
else {
|
| 210 |
_session_limit_disconnect($form_state['values']['sid']);
|
| 211 |
|
| 212 |
drupal_set_message(t('Session has been disconnected.'));
|
| 213 |
// redirect to main page.
|
| 214 |
drupal_goto();
|
| 215 |
}
|
| 216 |
}
|
| 217 |
|
| 218 |
/**
|
| 219 |
* Logout a specific session id and leave them a message.
|
| 220 |
*/
|
| 221 |
function _session_limit_disconnect($sid) {
|
| 222 |
$logout_message = <<<EOM
|
| 223 |
You have been automatically logged out.
|
| 224 |
Someone else has logged in with your username and password and the maximum number of @num simultaneous sessions was exceeded.
|
| 225 |
This may indicate that your account has been compromised or that account sharing is not allowed on this site.
|
| 226 |
Please contact the site administrator if you suspect your account has been compromised.
|
| 227 |
EOM;
|
| 228 |
$logout_message = 'messages|'. serialize(array('error' => array(t($logout_message, array('@num' => variable_get('session_limit_max', 1))))));
|
| 229 |
db_query("UPDATE {sessions} SET uid = 0, session = '%s' WHERE sid = '%s'", $logout_message, $sid);
|
| 230 |
}
|