Преглед изворни кода

+ Added token checks to Profile (Profile.php, Profile-Modify.php, Profile Template)
+ Added a session check to moderation center like the admin center (ModerationCenter.php, ModerationCenter language)
+ Added token checks to Moderation Center (Many files)

Spuds пре 12 година
родитељ
комит
6708364ab6

+ 6 - 0
Sources/Groups.php

@@ -450,6 +450,7 @@ function MembergroupMembers()
 	if (isset($_POST['remove']) && !empty($_REQUEST['rem']) && is_array($_REQUEST['rem']) && $context['group']['assignable'])
 	{
 		checkSession();
+		validateToken('mod-mgm');
 
 		// Make sure we're dealing with integers only.
 		foreach ($_REQUEST['rem'] as $key => $group)
@@ -462,6 +463,7 @@ function MembergroupMembers()
 	elseif (isset($_REQUEST['add']) && (!empty($_REQUEST['toAdd']) || !empty($_REQUEST['member_add'])) && $context['group']['assignable'])
 	{
 		checkSession();
+		validateToken('mod-mgm');
 
 		$member_query = array();
 		$member_parameters = array();
@@ -615,6 +617,7 @@ function MembergroupMembers()
 	// Select the template.
 	$context['sub_template'] = 'group_members';
 	$context['page_title'] = $txt['membergroups_members_title'] . ': ' . $context['group']['name'];
+	createToken('mod-mgm');
 }
 
 /**
@@ -640,6 +643,7 @@ function GroupRequests()
 	if (isset($_POST[$context['session_var']]) && !empty($_POST['groupr']) && !empty($_POST['req_action']))
 	{
 		checkSession('post');
+		validateToken('mod-gr');
 
 		// Clean the values.
 		foreach ($_POST['groupr'] as $k => $request)
@@ -889,6 +893,7 @@ function GroupRequests()
 			'hidden_fields' => array(
 				$context['session_var'] => $context['session_id'],
 			),
+			'token' => 'mod-gr',
 		),
 		'additional_rows' => array(
 			array(
@@ -908,6 +913,7 @@ function GroupRequests()
 	);
 
 	// Create the request list.
+	createToken('mod-gr');
 	createList($listOptions);
 
 	$context['default_list'] = 'group_request_list';

+ 1 - 0
Sources/ManageSettings.php

@@ -511,6 +511,7 @@ function ModifyGeneralSecuritySettings($return_config = false)
 			array('check', 'enableErrorLogging'),
 			array('check', 'enableErrorQueryLogging'),
 			array('check', 'securityDisable'),
+			array('check', 'securityDisable_moderate'),
 		'',
 			// Reactive on email, and approve on delete
 			array('check', 'send_validation_onChange'),

+ 52 - 0
Sources/ModerationCenter.php

@@ -145,10 +145,32 @@ function ModerationMain($dont_call = false)
 					'label' => $txt['mc_settings'],
 					'function' => 'ModerationSettings',
 				),
+				'modlogoff' => array(
+					'label' => $txt['mc_logoff'],
+					'function' => 'ModEndSession',
+				),
 			),
 		),
 	);
 
+	// Any files to include for moderation?
+	if (!empty($modSettings['integrate_moderate_include']))
+	{
+		$moderate_includes = explode(',', $modSettings['integrate_moderate_include']);
+		foreach ($moderate_includes as $include)
+		{
+			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
+			if (file_exists($include))
+				require_once($include);
+		}
+	}
+
+	// Let them modify admin areas easily.
+	call_integration_hook('integrate_moderate_areas', array(&$moderation_areas));
+
+	// Make sure the administrator has a valid session...
+	validateSession('moderate');
+
 	// I don't know where we're going - I don't know where we've been...
 	$menuOptions = array(
 		'action' => 'moderate',
@@ -1596,9 +1618,13 @@ function ViewWarningLog()
 				),
 			),
 		),
+		'form' => array(
+			'token' => 'mod-wt',
+		),
 	);
 
 	// Create the watched user list.
+	createToken('mod-wt');
 	createList($listOptions);
 
 	$context['sub_template'] = 'show_list';
@@ -1681,6 +1707,7 @@ function ViewWarningTemplates()
 	elseif (isset($_POST['delete']) && !empty($_POST['deltpl']))
 	{
 		checkSession('post');
+		validateToken('mod-wt');
 
 		// Log the actions.
 		$request = $smcFunc['db_query']('', '
@@ -1795,6 +1822,7 @@ function ViewWarningTemplates()
 		),
 		'form' => array(
 			'href' => $scripturl . '?action=moderate;area=warnings;sa=templates',
+			'token' => 'mod-wt',
 		),
 		'additional_rows' => array(
 			array(
@@ -1808,6 +1836,7 @@ function ViewWarningTemplates()
 	);
 
 	// Create the watched user list.
+	createToken('mod-wt');
 	createList($listOptions);
 
 	$context['sub_template'] = 'show_list';
@@ -1936,6 +1965,7 @@ function ModifyWarningTemplate()
 	if (isset($_POST['save']))
 	{
 		checkSession('post');
+		validateToken('mod-wt');
 
 		// To check the BBC is pretty good...
 		require_once($sourcedir . '/Subs-Post.php');
@@ -2012,6 +2042,8 @@ function ModifyWarningTemplate()
 		// Get out of town...
 		redirectexit('action=moderate;area=warnings;sa=templates');
 	}
+
+	createToken('mod-wt');
 }
 
 /**
@@ -2055,6 +2087,8 @@ function ModerationSettings()
 	if (isset($_POST['save']))
 	{
 		checkSession('post');
+		validateToken('mod-set');
+
 		/* Current format of mod_prefs is:
 			x|ABCD|yyy
 
@@ -2103,6 +2137,24 @@ function ModerationSettings()
 		'notify_approval' => $pref_binary & 4,
 		'user_blocks' => str_split($mod_blocks),
 	);
+
+	createToken('mod-set');
+}
+
+/**
+ * This ends a moderator session, requiring authentication to access the MCP again.
+ */
+function ModEndSession()
+{
+	// This is so easy!
+	unset($_SESSION['moderate_time']);
+
+	// Clean any moderator tokens as well.
+	foreach ($_SESSION['token'] as $key => $token)
+		if (strpos($key, '-mod') !== false)
+			unset($_SESSION['token'][$key]);
+
+	redirectexit('?action=moderate');
 }
 
 ?>

+ 5 - 0
Sources/Profile-Modify.php

@@ -1338,6 +1338,8 @@ function editBuddies($memID)
 	}
 	elseif (isset($_POST['new_buddy']))
 	{
+		checkSession();
+
 		// Prepare the string for extraction...
 		$_POST['new_buddy'] = strtr($smcFunc['htmlspecialchars']($_POST['new_buddy'], ENT_QUOTES), array('"' => '"'));
 		preg_match_all('~"([^"]+)"~', $_POST['new_buddy'], $matches);
@@ -1449,6 +1451,7 @@ function editIgnoreList($memID)
 	}
 	elseif (isset($_POST['new_ignore']))
 	{
+		checkSession();
 		// Prepare the string for extraction...
 		$_POST['new_ignore'] = strtr($smcFunc['htmlspecialchars']($_POST['new_ignore'], ENT_QUOTES), array('"' => '"'));
 		preg_match_all('~"([^"]+)"~', $_POST['new_ignore'], $matches);
@@ -1866,6 +1869,7 @@ function notification($memID)
 				'sa' => $context['menu_item_selected'],
 				$context['session_var'] => $context['session_id'],
 			),
+			'token' => $context['token_check'],
 		),
 		'additional_rows' => array(
 			array(
@@ -1982,6 +1986,7 @@ function notification($memID)
 				'sa' => $context['menu_item_selected'],
 				$context['session_var'] => $context['session_id'],
 			),
+			'token' => $context['token_check'],
 		),
 		'additional_rows' => array(
 			array(

+ 31 - 0
Sources/Profile.php

@@ -160,6 +160,7 @@ function ModifyProfile($post_errors = array())
 					'function' => 'account',
 					'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))),
 					'sc' => 'post',
+					'token' => 'profile-ac%u',
 					'password' => true,
 					'permission' => array(
 						'own' => array('profile_identity_any', 'profile_identity_own', 'manage_membergroups'),
@@ -171,6 +172,7 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Modify.php',
 					'function' => 'forumProfile',
 					'sc' => 'post',
+					'token' => 'profile-fp%u',
 					'permission' => array(
 						'own' => array('profile_extra_any', 'profile_extra_own', 'profile_title_own', 'profile_title_any'),
 						'any' => array('profile_extra_any', 'profile_title_any'),
@@ -181,6 +183,7 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Modify.php',
 					'function' => 'theme',
 					'sc' => 'post',
+					'token' => 'profile-th%u',
 					'permission' => array(
 						'own' => array('profile_extra_any', 'profile_extra_own'),
 						'any' => array('profile_extra_any'),
@@ -192,6 +195,7 @@ function ModifyProfile($post_errors = array())
 					'function' => 'authentication',
 					'enabled' => !empty($modSettings['enableOpenID']) || !empty($cur_profile['openid_uri']),
 					'sc' => 'post',
+					'token' => 'profile-au%u',
 					'hidden' => empty($modSettings['enableOpenID']) && empty($cur_profile['openid_uri']),
 					'password' => true,
 					'permission' => array(
@@ -204,6 +208,7 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Modify.php',
 					'function' => 'notification',
 					'sc' => 'post',
+					'token' => 'profile-nt%u',
 					'permission' => array(
 						'own' => array('profile_extra_any', 'profile_extra_own'),
 						'any' => array('profile_extra_any'),
@@ -216,6 +221,7 @@ function ModifyProfile($post_errors = array())
 					'function' => 'pmprefs',
 					'enabled' => allowedTo(array('profile_extra_own', 'profile_extra_any')),
 					'sc' => 'post',
+					'token' => 'profile-pm%u',
 					'permission' => array(
 						'own' => array('pm_read'),
 						'any' => array('profile_extra_any'),
@@ -227,6 +233,7 @@ function ModifyProfile($post_errors = array())
 					'function' => 'ignoreboards',
 					'enabled' => !empty($modSettings['allow_ignore_boards']),
 					'sc' => 'post',
+					'token' => 'profile-ib%u',
 					'permission' => array(
 						'own' => array('profile_extra_any', 'profile_extra_own'),
 						'any' => array('profile_extra_any'),
@@ -238,6 +245,7 @@ function ModifyProfile($post_errors = array())
 					'function' => 'editBuddyIgnoreLists',
 					'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'],
 					'sc' => 'post',
+					'token' => 'profile-bl%u',
 					'subsections' => array(
 						'buddies' => array($txt['editBuddies']),
 						'ignore' => array($txt['editIgnoreList']),
@@ -253,6 +261,8 @@ function ModifyProfile($post_errors = array())
 					'function' => 'groupMembership',
 					'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'],
 					'sc' => 'request',
+					'token' => 'profile-gm%u',
+					'token_type' => 'request',
 					'permission' => array(
 						'own' => array('profile_view_own'),
 						'any' => array('manage_membergroups'),
@@ -276,6 +286,7 @@ function ModifyProfile($post_errors = array())
 					'enabled' => in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1 && (!$context['user']['is_owner'] || $context['user']['is_admin']),
 					'file' => 'Profile-Actions.php',
 					'function' => 'issueWarning',
+					'token' => 'profile-iw%u',
 					'permission' => array(
 						'own' => array('issue_warning'),
 						'any' => array('issue_warning'),
@@ -305,6 +316,7 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Actions.php',
 					'function' => 'deleteAccount',
 					'sc' => 'post',
+					'token' => 'profile-da%u',
 					'password' => true,
 					'permission' => array(
 						'own' => array('profile_remove_any', 'profile_remove_own'),
@@ -315,6 +327,7 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Actions.php',
 					'function' => 'activateAccount',
 					'sc' => 'get',
+					'token' => 'profile-aa%u',
 					'select' => 'summary',
 					'permission' => array(
 						'own' => array(),
@@ -401,6 +414,15 @@ function ModifyProfile($post_errors = array())
 					$context['completed_save'] = true;
 				}
 
+				// Do we need to perform a token check?
+				if (!empty($area['token']))
+				{
+					$security_checks[isset($_REQUEST['save']) ? 'validateToken' : 'needsToken'] = $area['token'];
+					$token_name = $area['token'] !== true ? str_replace('%u', $context['id_member'], $area['token']) : 'profile-u' . $context['id_member'];
+
+					$token_type = isset($area['token_type']) && in_array($area['token_type'], array('request', 'post', 'get')) ? $area['token_type'] : 'post';
+				}
+
 				// Does this require session validating?
 				if (!empty($area['validate']))
 					$security_checks['validate'] = true;
@@ -427,9 +449,18 @@ function ModifyProfile($post_errors = array())
 		checkSession($security_checks['session']);
 	if (isset($security_checks['validate']))
 		validateSession();
+	if (isset($security_checks['validateToken']))
+		validateToken($token_name, $token_type);
 	if (isset($security_checks['permission']))
 		isAllowedTo($security_checks['permission']);
 
+	// Create a token if needed.
+	if (isset($security_checks['needsToken']))
+	{
+		createToken($token_name, $token_type);
+		$context['token_check'] = $token_name;
+	}
+
 	// File to include?
 	if (isset($profile_include_data['file']))
 		require_once($sourcedir . '/' . $profile_include_data['file']);

+ 17 - 12
Sources/Security.php

@@ -101,46 +101,51 @@ if (!defined('SMF'))
 */
 
 // Check if the user is who he/she says he is
-function validateSession()
+function validateSession($type = 'admin')
 {
 	global $modSettings, $sourcedir, $user_info, $sc, $user_settings;
 
 	// We don't care if the option is off, because Guests should NEVER get past here.
 	is_not_guest();
 
+	// Validate what type of session check this is.
+	$types = array();
+	call_integration_hook('integrate_validateSession', array($types));
+	$type = in_array($type, $types) || $type == 'moderate' ? $type : 'admin';
+
 	// If we're using XML give an additional ten minutes grace as an admin can't log on in XML mode.
 	$refreshTime = isset($_GET['xml']) ? 4200 : 3600;
 
 	// Is the security option off?  Or are they already logged in?
-	if (!empty($modSettings['securityDisable']) || (!empty($_SESSION['admin_time']) && $_SESSION['admin_time'] + $refreshTime >= time()))
+	if (!empty($modSettings['securityDisable' . $type != 'admin' ? '_' . $type : '']) || (!empty($_SESSION[$type . '_time']) && $_SESSION[$type . '_time'] + $refreshTime >= time()))
 		return;
 
 	require_once($sourcedir . '/Subs-Auth.php');
 
 	// Hashed password, ahoy!
-	if (isset($_POST['admin_hash_pass']) && strlen($_POST['admin_hash_pass']) == 40)
+	if (isset($_POST[$type . '_hash_pass']) && strlen($_POST[$type . '_hash_pass']) == 40)
 	{
 		checkSession();
 
-		$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST['admin_hash_pass'], true)), true);
+		$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST[$type . '_hash_pass'], true)), true);
 
-		if ($good_password || $_POST['admin_hash_pass'] == sha1($user_info['passwd'] . $sc))
+		if ($good_password || $_POST[$type . '_hash_pass'] == sha1($user_info['passwd'] . $sc))
 		{
-			$_SESSION['admin_time'] = time();
+			$_SESSION[$type . '_time'] = time();
 			return;
 		}
 	}
 	// Posting the password... check it.
-	if (isset($_POST['admin_pass']))
+	if (isset($_POST[$type. '_pass']))
 	{
 		checkSession();
 
-		$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST['admin_pass'], false)), true);
+		$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST[$type . '_pass'], false)), true);
 
 		// Password correct?
-		if ($good_password || sha1(strtolower($user_info['username']) . $_POST['admin_pass']) == $user_info['passwd'])
+		if ($good_password || sha1(strtolower($user_info['username']) . $_POST[$type . '_pass']) == $user_info['passwd'])
 		{
-			$_SESSION['admin_time'] = time();
+			$_SESSION[$type . '_time'] = time();
 			return;
 		}
 	}
@@ -150,12 +155,12 @@ function validateSession()
 		require_once($sourcedir . '/Subs-OpenID.php');
 		smf_openID_revalidate();
 
-		$_SESSION['admin_time'] = time();
+		$_SESSION[$type . '_time'] = time();
 		return;
 	}
 
 	// Need to type in a password for that, man.
-	adminLogin();
+	adminLogin($type);
 }
 
 // Require a user who is logged in. (not a guest.)

+ 14 - 6
Sources/Subs-Auth.php

@@ -251,23 +251,28 @@ function InMaintenance()
 	$context['page_title'] = $txt['maintain_mode'];
 }
 
-function adminLogin()
+function adminLogin($type = 'admin')
 {
 	global $context, $scripturl, $txt, $user_info, $user_settings;
 
 	loadLanguage('Admin');
 	loadTemplate('Login');
 
+	// Validate what type of session check this is.
+	$types = array();
+	call_integration_hook('integrate_validateSession', array($types));
+	$type = in_array($type, $types) || $type == 'moderate' ? $type : 'admin';
+
 	// They used a wrong password, log it and unset that.
-	if (isset($_POST['admin_hash_pass']) || isset($_POST['admin_pass']))
+	if (isset($_POST[$type . '_hash_pass']) || isset($_POST[$type . '_pass']))
 	{
 		$txt['security_wrong'] = sprintf($txt['security_wrong'], isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : $txt['unknown'], $_SERVER['HTTP_USER_AGENT'], $user_info['ip']);
 		log_error($txt['security_wrong'], 'critical');
 
-		if (isset($_POST['admin_hash_pass']))
-			unset($_POST['admin_hash_pass']);
-		if (isset($_POST['admin_pass']))
-			unset($_POST['admin_pass']);
+		if (isset($_POST[$type . '_hash_pass']))
+			unset($_POST[$type . '_hash_pass']);
+		if (isset($_POST[$type . '_pass']))
+			unset($_POST[$type . '_pass']);
 
 		$context['incorrect_password'] = true;
 	}
@@ -290,6 +295,9 @@ function adminLogin()
 	if (!isset($context['page_title']))
 		$context['page_title'] = $txt['login'];
 
+	// The type of action.
+	$context['sessionCheckType'] = $type;
+
 	obExit();
 
 	// We MUST exit at this point, because otherwise we CANNOT KNOW that the user is privileged.

+ 3 - 3
Themes/default/Login.template.php

@@ -205,7 +205,7 @@ function template_admin_login()
 	echo '
 <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 
-<form action="', $scripturl, $context['get_data'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin" onsubmit="hashAdminPassword(this, \'', $context['user']['username'], '\', \'', $context['session_id'], '\');">
+<form action="', $scripturl, $context['get_data'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin" onsubmit="hash', ucfirst($context['sessionCheckType']), 'Password(this, \'', $context['user']['username'], '\', \'', $context['session_id'], '\');">
 	<div class="tborder login" id="admin_login">
 		<div class="cat_bar">
 			<h3 class="catbg">
@@ -221,7 +221,7 @@ function template_admin_login()
 
 	echo '
 			<strong>', $txt['password'], ':</strong>
-			<input type="password" name="admin_pass" size="24" class="input_password" />
+			<input type="password" name="', $context['sessionCheckType'], '_pass" size="24" class="input_password" />
 			<a href="', $scripturl, '?action=helpadmin;help=securityDisable_why" onclick="return reqWin(this.href);" class="help"><img src="', $settings['images_url'], '/helptopics.gif" alt="', $txt['help'], '" /></a><br />
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 			<input type="hidden" name="', $context['admin-login_token_var'], '" value="', $context['admin-login_token'], '" />
@@ -232,7 +232,7 @@ function template_admin_login()
 		</div>
 		<span class="lowerframe"><span></span></span>
 	</div>
-	<input type="hidden" name="admin_hash_pass" value="" />
+	<input type="hidden" name="', $context['sessionCheckType'], '_hash_pass" value="" />
 </form>';
 
 	// Focus on the password box.

+ 1 - 0
Themes/default/ManageMembergroups.template.php

@@ -544,6 +544,7 @@ function template_group_members()
 
 	echo '
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+			<input type="hidden" name="', $context['mod-mgm_token_var'], '" value="', $context['mod-mgm_token'], '" />
 		</form>
 	</div>
 	<br class="clear" />';

+ 2 - 0
Themes/default/ModerationCenter.template.php

@@ -754,6 +754,7 @@ function template_moderation_settings()
 					</dl>
 					<div class="righttext">
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+						<input type="hidden" name="', $context['mod-set_token_var'], '" value="', $context['mod-set_token'], '" />
 						<input type="submit" name="save" value="', $txt['save'], '" class="button_submit" />
 					</div>
 				</div>
@@ -852,6 +853,7 @@ function template_warn_template()
 				<span class="botslice"><span></span></span>
 			</div>
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+			<input type="hidden" name="', $context['mod-wt_token_var'], '" value="', $context['mod-wt_token'], '" />
 		</form>
 	</div>
 	<br class="clear" />';

+ 65 - 10
Themes/default/Profile.template.php

@@ -546,7 +546,15 @@ function template_editBuddies()
 					<label for="new_buddy">
 						<strong>', $txt['who_member'], ':</strong>
 					</label>
-					<input type="text" name="new_buddy" id="new_buddy" size="25" class="input_text" />
+					<input type="text" name="new_buddy" id="new_buddy" size="25" class="input_text" />';
+
+	if (!empty($context['token_check']))
+		echo '
+						<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+	echo '
+					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+
 					<input type="submit" value="', $txt['buddy_add_button'], '" class="button_submit" />
 			</div>
 			<span class="lowerframe"><span></span></span>
@@ -632,7 +640,14 @@ function template_editIgnoreList()
 					<label for="new_buddy">
 						<strong>', $txt['who_member'], ':</strong>
 					</label>
-					<input type="text" name="new_ignore" id="new_ignore" size="25" class="input_text" />
+					<input type="text" name="new_ignore" id="new_ignore" size="25" class="input_text" />';
+
+	if (!empty($context['token_check']))
+		echo '
+						<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+	echo '
+					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="submit" value="', $txt['ignore_add_button'], '" class="button_submit" />
 			</div>
 			<span class="lowerframe"><span></span></span>
@@ -1323,6 +1338,10 @@ function template_edit_options()
 		echo '
 						<input type="submit" value="', $txt['change_profile'], '" class="button_submit" />';
 
+	if (!empty($context['token_check']))
+		echo '
+						<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
 	echo '
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 						<input type="hidden" name="u" value="', $context['id_member'], '" />
@@ -1634,7 +1653,13 @@ function template_notification()
 						</select><br class="clear" />
 
 						<div>
-							<input id="notify_submit" type="submit" value="', $txt['notify_save'], '" class="button_submit floatright" />
+							<input id="notify_submit" type="submit" value="', $txt['notify_save'], '" class="button_submit floatright" />';
+
+	if (!empty($context['token_check']))
+		echo '
+						<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+	echo '
 							<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 							<input type="hidden" name="u" value="', $context['id_member'], '" />
 							<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
@@ -1728,7 +1753,7 @@ function template_groupMembership()
 				// Can they leave their group?
 				if ($group['can_leave'])
 					echo '
-							<a href="' . $scripturl . '?action=profile;save;u=' . $context['id_member'] . ';area=groupmembership;' . $context['session_var'] . '=' . $context['session_id'] . ';gid=' . $group['id'] . '">' . $txt['leave_group'] . '</a>';
+							<a href="' . $scripturl . '?action=profile;save;u=' . $context['id_member'] . ';area=groupmembership;' . $context['session_var'] . '=' . $context['session_id'] . ';gid=' . $group['id'] . ';', $context[$context['token_check'] . '_token_var'], '=', $context[$context['token_check'] . '_token'], '">' . $txt['leave_group'] . '</a>';
 				echo '
 						</td>
 					</tr>';
@@ -1773,7 +1798,7 @@ function template_groupMembership()
 
 				if ($group['type'] == 3)
 					echo '
-							<a href="', $scripturl, '?action=profile;save;u=', $context['id_member'], ';area=groupmembership;', $context['session_var'], '=', $context['session_id'], ';gid=', $group['id'], '">', $txt['join_group'], '</a>';
+							<a href="', $scripturl, '?action=profile;save;u=', $context['id_member'], ';area=groupmembership;', $context['session_var'], '=', $context['session_id'], ';gid=', $group['id'], ';', $context[$context['token_check'] . '_token_var'], '=', $context[$context['token_check'] . '_token'], '">', $txt['join_group'], '</a>';
 				elseif ($group['type'] == 2 && $group['pending'])
 					echo '
 							', $txt['approval_pending'];
@@ -1781,6 +1806,8 @@ function template_groupMembership()
 					echo '
 							<a href="', $scripturl, '?action=profile;u=', $context['id_member'], ';area=groupmembership;request=', $group['id'], '">', $txt['request_group'], '</a>';
 
+//				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
 				echo '
 						</td>
 					</tr>';
@@ -1814,6 +1841,10 @@ function template_groupMembership()
 	// ]]></script>';
 	}
 
+	if (!empty($context['token_check']))
+		echo '
+				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
 	echo '
 				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 				<input type="hidden" name="u" value="', $context['id_member'], '" />
@@ -2231,7 +2262,13 @@ function template_issueWarning()
 	}
 	echo '
 				</dl>
-				<div class="righttext">
+				<div class="righttext">';
+
+	if (!empty($context['token_check']))
+		echo '
+				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+	echo '
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 					<input type="submit" name="save" value="', $context['user']['is_owner'] ? $txt['change_profile'] : $txt['profile_warning_issue'], '" class="button_submit" />
 				</div>
@@ -2345,7 +2382,13 @@ function template_deleteAccount()
 					<div>
 						<strong', (isset($context['modify_error']['bad_password']) || isset($context['modify_error']['no_password']) ? ' class="error"' : ''), '>', $txt['current_password'], ': </strong>
 						<input type="password" name="oldpasswrd" size="20" class="input_password" />&nbsp;&nbsp;&nbsp;&nbsp;
-						<input type="submit" value="', $txt['yes'], '" class="button_submit" />
+						<input type="submit" value="', $txt['yes'], '" class="button_submit" />';
+
+		if (!empty($context['token_check']))
+			echo '
+				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+		echo '
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 						<input type="hidden" name="u" value="', $context['id_member'], '" />
 						<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
@@ -2374,7 +2417,13 @@ function template_deleteAccount()
 						<label for="deleteAccount"><input type="checkbox" name="deleteAccount" id="deleteAccount" value="1" class="input_check" onclick="if (this.checked) return confirm(\'', $txt['deleteAccount_confirm'], '\');" /> ', $txt['deleteAccount_member'], '.</label>
 					</div>
 					<div>
-						<input type="submit" value="', $txt['delete'], '" class="button_submit" />
+						<input type="submit" value="', $txt['delete'], '" class="button_submit" />';
+
+		if (!empty($context['token_check']))
+			echo '
+				<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+		echo '
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 						<input type="hidden" name="u" value="', $context['id_member'], '" />
 						<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />
@@ -2916,8 +2965,14 @@ function template_authentication_method()
 						</dd>
 					</dl>';
 
-echo '
-					<div class="righttext">
+	echo '
+					<div class="righttext">';
+
+	if (!empty($context['token_check']))
+		echo '
+						<input type="hidden" name="', $context[$context['token_check'] . '_token_var'], '" value="', $context[$context['token_check'] . '_token'], '" />';
+
+	echo '
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
 						<input type="hidden" name="u" value="', $context['id_member'], '" />
 						<input type="hidden" name="sa" value="', $context['menu_item_selected'], '" />

+ 1 - 0
Themes/default/languages/ModerationCenter.english.php

@@ -137,6 +137,7 @@ $txt['mc_prefs_notify_report_never'] = 'Never';
 $txt['mc_prefs_notify_report_moderator'] = 'Only if it\'s a board I moderate';
 $txt['mc_prefs_notify_report_always'] = 'Always';
 $txt['mc_prefs_notify_approval'] = 'Notify of items awaiting approval';
+$txt['mc_logoff'] = 'Moderator End Session';
 
 // Use entities in the below string.
 $txt['mc_click_add_note'] = 'Add a new note';

+ 9 - 0
Themes/default/scripts/script.js

@@ -656,6 +656,15 @@ function hashAdminPassword(doForm, username, cur_session_id)
 	doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*');
 }
 
+function hashModeratePassword(doForm, username, cur_session_id)
+{
+	if (typeof(hex_sha1) == 'undefined')
+		return;
+
+	doForm.moderate_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.moderate_pass.value.php_to8bit()) + cur_session_id);
+	doForm.moderate_pass.value = doForm.moderate_pass.value.replace(/./g, '*');
+}
+
 // Shows the page numbers by clicking the dots (in compact view).
 function expandPages(spanNode, baseURL, firstPage, lastPage, perPage)
 {