Permalink
Browse files

Item14506: Support Bulk-reset with login tokens

Also fix a bug - token login needed to convert from cUID back to Login
Name.
  • Loading branch information...
gac410 committed Oct 28, 2017
1 parent c4dca93 commit eddde6dcf901a83371e56b5b773d1cb21387bc01
@@ -1,4 +1,4 @@
%META:TOPICINFO{author="ProjectContributor" date="1487907314" format="1.1" version="1"}%
%META:TOPICINFO{author="ProjectContributor" date="1509158993" format="1.1" version="1"}%
%META:TOPICPARENT{name="AdminToolsCategory"}%
---+ Bulk Reset Passwords
@@ -12,9 +12,14 @@ load the page for all users [[BulkResetPassword?pagesize=;pager=off][click here]
*Follow these two steps:*
<form action="%SCRIPTURLPATH{"manage"}%/%WEB%/%TOPIC%" method="post">
<form action='%SCRIPTURLPATH{"rest"}%/PasswordManagementPlugin/bulkResetPassword' method='post'>
<div class='foswikiFormSteps'>
<div class='foswikiFormStep'>
---+++ Reset Token Lifetime
Enter the time in minutes that the password reset login token should be valid.
<input type='number' name='validFor' value='%QUERY{"{Login}{TokenLifetime}"}%' length=5>
</div>
<div class='foswikiFormStep'>
---+++ Select users [<a onclick="javascript:$('input[name="LoginName"]').each(function(_,v){$(v).attr('checked',!$(v).attr('checked'))})">Toggle all</a>]
%IF{
"context passwords_modifyable"
@@ -30,7 +35,7 @@ load the page for all users [[BulkResetPassword?pagesize=;pager=off][click here]
web="%USERSWEB%"
type="query"
header="|*WikiName* | *Reset* |"
format="| $web.$topic | <input %NOTMODIFYABLE% type='checkbox' name='LoginName' value='$topic'> |"
format="| $web.$topic | <input %NOTMODIFYABLE% type='checkbox' name='resetUsers' value='$topic'> |"
nosearch="on" nototal="on"
pager="%URLPARAM{"pager" default="on"}%"
%URLPARAM{"pagesize" default="pagesize=\"%DEFAULTPAGESIZE%\"}%
@@ -130,7 +130,8 @@ sub _RESTresetPassword {
);
}
my ( $sent, $errors ) = _generateResetEmail( $session, $user, \@em );
# lifetime 0 - uses configured default
my ( $sent, $errors ) = _generateResetEmail( $session, $user, 0, \@em, '' );
# Now that we have successfully reset the password we log the event
$session->logger->log(
@@ -149,7 +150,10 @@ sub _RESTresetPassword {
status => 200,
topic => $Foswiki::cfg{HomeTopicName},
def => 'reset_ok',
params => [ $Foswiki::cfg{Login}{TokenLifetime} || 900, $errors ]
params => [
$Foswiki::cfg{Login}{TokenLifetime} || 15,
($errors) ? '1' : '0'
]
);
}
else {
@@ -213,6 +217,9 @@ sub _RESTbulkResetPassword {
throw Foswiki::OopsException( 'password', def => 'no_users_to_reset' );
}
my $validFor = $query->param('validFor') || 0;
my $Introduction = $query->param('Introduction') || '';
my ( $sent, $errors );
foreach my $userName (@userNames) {
@@ -239,7 +246,9 @@ sub _RESTbulkResetPassword {
);
}
( $sent, $errors ) = _generateResetEmail( $session, $user, \@em );
( $sent, $errors ) =
_generateResetEmail( $session, $user, $validFor, \@em,
$Introduction );
# Now that we have successfully reset the password we log the event
$session->logger->log(
@@ -259,7 +268,10 @@ sub _RESTbulkResetPassword {
status => 200,
topic => $Foswiki::cfg{HomeTopicName},
def => 'reset_ok',
params => [ $Foswiki::cfg{Login}{TokenLifetime} || 900, $errors ]
params => [
$validFor || $Foswiki::cfg{Login}{TokenLifetime} || 15,
($errors) ? '1' : '0'
]
);
}
else {
@@ -274,7 +286,7 @@ sub _RESTbulkResetPassword {
=begin TML
---++ StaticMethod _generateResetEmail ( $session, $user, $emails )
---++ StaticMethod _generateResetEmail ( $session, $user, $validFor, $emails )
Utility method. Passed a user name and list of emails, generate the reset token
and email it to the email addresses for that user. This is intended for sending
@@ -286,16 +298,16 @@ agents can fail the entire email.
=cut
sub _generateResetEmail {
my $session = shift;
my $user = shift;
my $emails = shift;
my ( $session, $user, $validFor, $emails, $message ) = @_;
my $users = $session->{users};
# TOPICRESTRICTION - locks session down to a single topic
# PASSWORDRESET - Bypasses checking of old password.
my $token = Foswiki::LoginManager::generateLoginToken(
$user,
$validFor,
{
FOSWIKI_TOPICRESTRICTION =>
"$Foswiki::cfg{SystemWebName}.ChangePassword",
@@ -330,6 +342,7 @@ sub _generateResetEmail {
EmailAddress => $email,
TokenLife => $Foswiki::cfg{Login}{TokenLifetime} || 900,
AuthToken => $token,
Introduction => $message,
}
);
@@ -341,7 +354,6 @@ sub _generateResetEmail {
}
$session->leaveContext('absolute_urls');
}
return ( $sent, $errors );
}
@@ -1,3 +1,5 @@
!noci
data/System/BulkResetPassword.txt 0644
data/System/ChangeEmailAddress.txt 0664
data/System/ChangePassword.txt 0664
data/System/PasswordManagementPlugin.txt 0664
@@ -139,11 +139,13 @@
%{==============================================================================}%
%TMPL:DEF{"reset_ok"}%
%MAKETEXT{"An email has been sent to the address on file for this account. You can use the link in that message for one-time access to the [[[_1]][PasswordChange]] topic, where you can change your password." arg1="%SYSTEMWEB%.ChangePassword" }%
%MAKETEXT{"An email has been sent to one or more addresses on file for this account. You can use the link in that message for one-time access to the [[[_1]][PasswordChange]] topic, where you can change your password." arg1="%SYSTEMWEB%.ChangePassword" }%
%MAKETEXT{"This link will only be valid for a brief time. ([*,_1,second]). If you decide to not change your password, you may safely ignore the message and the old password will remain valid." arg1="%PARAM1%"}%
%MAKETEXT{"This link will only be valid for a brief time. ([*,_1,minute]). If you decide to not change your password, you may safely ignore the message and the old password will remain valid." arg1="%PARAM1%"}%
%PARAM2%
%IF{"'%PARAM2%'='1'" then="
%MAKETEXT{"Sending to one or more email addresses failed. If you do not receive a reset message, contact [_1] for assistance." arg1="%WIKIWEBMASTER%"}%
"}%
*[[%BASEWEB%.%BASETOPIC%][%MAKETEXT{"OK"}%]]*
%TMPL:END%
@@ -13,6 +13,8 @@ Content-Transfer-Encoding: 8bit
%MAKETEXT{"This link will only be valid for a brief time. ([*,_1,second]), and can only be used once!" arg1="%TOKENLIFE%"}%
%MAKETEXT{"If you decide to not change your password, or you did not request a reset, you may safely ignore this message and the old password will remain valid."}%
%INTRODUCTION%
%MAKETEXT{"Change your password at: [_1]" arg1="%SCRIPTURL{"view" topic="%SYSTEMWEB%.ChangePassword" authtoken="%AUTHTOKEN%"}% "}%
%MAKETEXT{"If you have any questions, please contact [_1]." args="%WIKIWEBMASTER%"}%
@@ -13,7 +13,6 @@ data/Main/UserListHeader.txt 0644
data/System/BulkRegistration.txt 0644
data/System/BulkRegistrationInputTemplate.txt 0644
data/System/BulkRegistrationInputViewTemplate.txt 0644
data/System/BulkResetPassword.txt 0644
data/System/ChangeEmailAddress.txt 0644
data/System/DefaultUserRegistration.txt 0644
data/System/FAQRebuildingWikiUsersTopic.txt 0644
View
@@ -417,10 +417,10 @@ $Foswiki::cfg{Validation}{ExpireKeyOnUse} = 1;
$Foswiki::cfg{LoginManager} = 'Foswiki::LoginManager::TemplateLogin';
# **NUMBER LABEL="Login Token Lifetime"**
# Specifiy the time in seconds the Login token should be usable, for example: password reset.
# Specifiy the time in minutes the Login token should be usable, for example: password reset.
# Recommend setting this to allow for email delays, including grey listing
# no more than 15 minutes (900 seconds).
$Foswiki::cfg{Login}{TokenLifetime} = 900;
# at least 15 minutes.
$Foswiki::cfg{Login}{TokenLifetime} = 15;
# **BOOLEAN LABEL="Debug Login Manager" EXPERT**
# Write debugging output to the webserver error log.
@@ -912,6 +912,7 @@ $Foswiki::cfg{AccessibleCFG} = [
'{LeaseLengthLessForceful}',
'{LinkProtocolPattern}',
'{LocalSitePreferences}',
'{Login}{TokenLifetime}',
'{LoginNameFilterIn}',
'{MaxRevisionsInADiff}',
'{MinPasswordLength}',
@@ -361,6 +361,8 @@ sub loadSession {
my $tokenUser = $this->_getTokenCredentials($session);
if ($tokenUser) {
$tokenUser =
$session->{users}->getLoginName($tokenUser); # Returns a cUID
_trace( $this,
"Replacing current user with $tokenUser from authtoken" );
$this->{_cgisession}->clear('SUDOFROMAUTHUSER');
@@ -1755,7 +1757,7 @@ sub removeUserSessions {
=begin TML
---++ StaticMethod generateLoginToken( $cUID, %sessionVars ) = $token
---++ StaticMethod generateLoginToken( $cUID, $validFor, %sessionVars ) = $token
Creates a login token and saves the authentication information into a corresponding
file. This will allow the user to gain access for purposes of resetting a password.
@@ -1766,6 +1768,8 @@ Note: The cUID contained in the token will be granted access, even if the user
not known to the Password Manager / Mapper. The caller of this function should
ensure that the user exists before creating the token.
Valid time in minutes, defaults to 15 minutes, as configured in $Foswiki::cfg{Login}{TokenLifetime}
The $sessionVars hash is used to set Session Variables. Options hash currently includes:
$ FOSWIKI_TOPICRESTRICTION => "Web.Topic": Access will be redirected to this topic
@@ -1778,16 +1782,17 @@ variables.
sub generateLoginToken {
my $cUID = shift;
my $options = shift;
my ( $cUID, $validFor, $options ) = @_;
$options->{cUID} = $cUID;
my $nonce = Foswiki::generateRandomChars(32);
my $token = Digest::MD5::md5_hex($nonce);
my $tokenFile = "$Foswiki::cfg{WorkingDir}/tmp/tokenauth_$token";
my $tokenLifetime = $Foswiki::cfg{Login}{TokenLifetime} || 900;
$options->{expires} = time() + $tokenLifetime;
my $nonce = Foswiki::generateRandomChars(32);
my $token = Digest::MD5::md5_hex($nonce);
my $tokenFile = "$Foswiki::cfg{WorkingDir}/tmp/tokenauth_$token";
$validFor ||= $Foswiki::cfg{Login}{TokenLifetime} || 15;
$options->{expires} = time() + ( $validFor * 60 );
# login token file is only written to once, so if it already exists,
# suspect a security hack (O_EXCL)

0 comments on commit eddde6d

Please sign in to comment.