add: Settings can now return errors and messages to the user

add: WebGUI Auth password recovery can now be done by profile fields or just e-mail address
This commit is contained in:
Doug Bell 2007-07-09 21:23:11 +00:00
parent efd1e6705b
commit 8674572035
6 changed files with 530 additions and 57 deletions

View file

@ -68,6 +68,9 @@
- Collaboration System wobjects can now be subclassed and still work with the
existing Thread and Post assets.
- fix: Added some additional i18n that was missing.
- add: Settings can now return error messages to the user
- add: Password recovery can now be based on profile fields or simply by the
user's e-mail address.
7.3.20
- fix: Deactivated Users Subscriptions (perlDreamer Consulting, LLC)

View file

@ -367,6 +367,22 @@ sub deleteParams {
#-------------------------------------------------------------------
=head2 deleteSingleParam ( )
Removes a single authentication parameter from the database.
=cut
sub deleteSingleParam {
my $self = shift;
my ($userId, $authMethod, $fieldName) = @_;
$self->session->db->write('delete from authentication where userId = ? and authMethod = ? and fieldName = ?', [$userId, $authMethod, $fieldName]);
}
#-------------------------------------------------------------------
=head2 displayAccount ( method [,vars] )
Superclass method that performs general functionality for viewing editable fields related to a user's account.

View file

@ -400,9 +400,10 @@ sub editUserFormSave {
=cut
sub editUserSettingsForm {
my $self = shift;
my $i18n = WebGUI::International->new($self->session,'AuthWebGUI');
my $f = WebGUI::HTMLForm->new($self->session);
my $self = shift;
my $i18n = WebGUI::International->new($self->session,'AuthWebGUI');
my $f = WebGUI::HTMLForm->new($self->session);
$f->integer(
-name=>"webguiPasswordLength",
-value=>$self->session->setting->get("webguiPasswordLength"),
@ -453,11 +454,14 @@ sub editUserSettingsForm {
-value=>$self->session->setting->get("webguiChangePassword"),
-label=>$i18n->get(18)
);
$f->yesNo(
$f->selectList(
-name => "webguiPasswordRecovery",
-value => $self->session->setting->get("webguiPasswordRecovery"),
-label => $i18n->get(6),
-hoverHelp => $i18n->get('webguiPasswordRecovery hoverHelp')
-hoverHelp => $i18n->get('webguiPasswordRecovery hoverHelp'),
-options => $self->getPasswordRecoveryTypesAvailable,
-size => 1,
-multiple => 0,
);
$f->yesNo(
-name => "webguiPasswordRecoveryRequireUsername",
@ -510,9 +514,11 @@ sub editUserSettingsForm {
#-------------------------------------------------------------------
sub editUserSettingsFormSave {
my $self = shift;
my $f = $self->session->form;
my $s = $self->session->setting;
my $self = shift;
my $f = $self->session->form;
my $s = $self->session->setting;
my $i18n = WebGUI::International->new($self->session, 'AuthWebGUI');
my @errors; # Array of errors to return, if any. See WebGUI::Operation::Settings->www_saveSettings
$s->set("webguiPasswordLength", $f->process("webguiPasswordLength","integer"));
$s->set("webguiRequiredDigits", $f->process("webguiRequiredDigits","integer"));
$s->set("webguiNonWordCharacters", $f->process("webguiNonWordCharacters","integer"));
@ -524,9 +530,27 @@ sub editUserSettingsFormSave {
$s->set("webguiChangeUsername", $f->process("webguiChangeUsername","yesNo"));
$s->set("webguiChangePassword", $f->process("webguiChangePassword","yesNo"));
# Special case to make sure we have at least one field enabled before allowing
# password recovery to be turned on.
$s->set("webguiPasswordRecovery", $f->process("webguiPasswordRecovery","yesNo") && ($self->session->db->quickArray("SELECT COUNT(*) FROM userProfileField WHERE requiredForPasswordRecovery = 1"))[0] > 0);
# Make sure we have the ability to recover a password if we're trying to
# enable password recovery
my $passwordRecoveryType = $f->process("webguiPasswordRecovery", "selectList");
if ($passwordRecoveryType eq "profile") {
# Profile recovery requires at least one field set to required
my ($passwordRecoveryFields)
= $self->session->db->quickArray(
"SELECT COUNT(*) FROM userProfileField WHERE requiredForPasswordRecovery = 1"
);
if ($passwordRecoveryFields <= 0) {
push @errors, $i18n->get("error passwordRecoveryType no profile fields required");
}
else {
$s->set("webguiPasswordRecovery", $passwordRecoveryType);
}
}
# Recovery types that need no error checking
else {
$s->set("webguiPasswordRecovery", $passwordRecoveryType);
}
$s->set("webguiPasswordRecoveryRequireUsername", $f->process("webguiPasswordRecoveryRequireUsername","yesNo"));
$s->set("webguiValidateEmail", $f->process("webguiValidateEmail","yesNo"));
@ -536,6 +560,13 @@ sub editUserSettingsFormSave {
$s->set("webguiExpiredPasswordTemplate", $f->process("webguiExpiredPasswordTemplate","template"));
$s->set("webguiLoginTemplate", $f->process("webguiLoginTemplate","template"));
$s->set("webguiPasswordRecoveryTemplate", $f->process("webguiPasswordRecoveryTemplate","template"));
if (@errors) {
return \@errors;
}
else {
return;
}
}
#-------------------------------------------------------------------
@ -564,10 +595,45 @@ sub getLoginTemplateId {
#-------------------------------------------------------------------
sub getPasswordRecoveryTemplateId {
my $self = shift;
return $self->session->setting->get("webguiPasswordRecoveryTemplate") || "PBtmpl0000000000000014";
my $self = shift;
return $self->session->setting->get("webguiPasswordRecoveryTemplate") || "PBtmpl0000000000000014";
}
#-------------------------------------------------------------------
sub getPasswordRecoveryType {
my $self = shift;
return $self->session->setting->get("webguiPasswordRecovery");
}
#----------------------------------------------------------------------------
=head2 getPasswordRecoveryTypesAvailable
Returns a hash reference of password recovery types. Keys are the type, values
are an i18n label for the user.
=cut
sub getPasswordRecoveryTypesAvailable {
my $self = shift;
my $i18n = WebGUI::International->new($self->session, 'AuthWebGUI');
tie my %types, 'Tie::IxHash', (
"" => $i18n->get("setting passwordRecoveryType none"),
profile => $i18n->get("setting passwordRecoveryType profile"),
email => $i18n->get("setting passwordRecoveryType email"),
);
return \%types;
}
#-------------------------------------------------------------------
sub getUserIdByPasswordRecoveryToken {
my $self = shift;
my $session = shift;
my $token = shift;
return $session->db->quickScalar("select userId from authentication where fieldName = 'emailRecoverPasswordVerificationNumber' and fieldData = ?", [$token]);
}
#-------------------------------------------------------------------
sub login {
@ -594,22 +660,88 @@ sub login {
#-------------------------------------------------------------------
sub new {
my $class = shift;
my $session = shift;
my $authMethod = $_[0];
my $userId = $_[1];
my @callable = ('validateEmail','createAccount','deactivateAccount','displayAccount','displayLogin','login','logout','recoverPassword','resetExpiredPassword','recoverPasswordFinish','createAccountSave','deactivateAccountConfirm','resetExpiredPasswordSave','updateAccount');
my $self = WebGUI::Auth->new($session,$authMethod,$userId,\@callable);
bless $self, $class;
my $class = shift;
my $session = shift;
my $authMethod = $_[0];
my $userId = $_[1];
my @callable = ('validateEmail','createAccount','deactivateAccount','displayAccount','displayLogin','login','logout','recoverPassword','resetExpiredPassword','recoverPasswordFinish','createAccountSave','deactivateAccountConfirm','resetExpiredPasswordSave','updateAccount', 'emailResetPassword', 'emailResetPasswordFinish');
my $self = WebGUI::Auth->new($session,$authMethod,$userId,\@callable);
bless $self, $class;
}
#-------------------------------------------------------------------
=head2 recoverPassword ( )
Initiates the password recovery process. Checks for recovery type, and then runs the appropriate method.
=cut
sub recoverPassword {
my $self = shift;
return $self->displayLogin unless $self->session->setting->get('webguiPasswordRecovery') and $self->userId eq '1';
my @fields = @{WebGUI::ProfileField->getPasswordRecoveryFields($self->session)};
return $self->displayLogin unless @fields;
my $self = shift;
return $self->displayLogin unless ($self->session->setting->get('webguiPasswordRecovery') ne '') and $self->userId eq '1';
my $type = $self->getPasswordRecoveryType;
#$self->session->errorHandler->warn("recovery type: $type");
if ($type eq 'profile') {
$self->profileRecoverPassword;
}
elsif ($type eq 'email') {
$self->emailRecoverPassword;
}
}
#-------------------------------------------------------------------
sub emailRecoverPassword {
my $self = shift;
my $i18n = WebGUI::International->new($self->session);
my $output
= "<h1>" . $i18n->get('recover password banner', 'AuthWebGUI') . " </h1> <br /> <br /> "
. "<h3>" . $i18n->get('email recover password start message', 'AuthWebGUI') ."</h3>"
;
my $f = WebGUI::HTMLForm->new($self->session);
$f->hidden(
name => 'op',
value => 'auth',
);
$f->hidden(
name => "method",
value => "recoverPasswordFinish",
);
$f->text(
name => "username",
label => $i18n->get('password recovery login label', 'AuthWebGUI'),
hoverHelp => $i18n->get('password recovery login help', 'AuthWebGUI'),
);
$f->email(
name =>"email",
label => $i18n->get('password recovery email label', 'AuthWebGUI'),
hoverHelp => $i18n->get('password recovery email help', 'AuthWebGUI'),
);
$f->submit();
$output .= $f->print;
return $output;
}
#-------------------------------------------------------------------
sub profileRecoverPassword {
my $self = shift;
my @fields = @{WebGUI::ProfileField->getPasswordRecoveryFields($self->session)};
return $self->displayLogin unless @fields;
my $vars = {};
my $i18n = WebGUI::International->new($self->session);
@ -647,16 +779,37 @@ sub recoverPassword {
return WebGUI::Asset::Template->new($self->session,$self->getPasswordRecoveryTemplateId)->process($vars);
}
#-------------------------------------------------------------------
=head2 recoverPasswordFinish ( )
Handles data for recovery of password. Gets password recovery type, and then runs the appropriate method.
=cut
sub recoverPasswordFinish {
my $self = shift;
my $i18n = WebGUI::International->new($self->session);
my $i18n2 = WebGUI::International->new($self->session, 'AuthWebGUI');
return $self->displayLogin unless $self->session->setting->get('webguiPasswordRecovery') and $self->userId eq '1';
my $self = shift;
my $username;
if ($self->getSetting('passwordRecoveryRequireUsername')) {
my $type = $self->getPasswordRecoveryType;
if ($type eq 'profile') {
$self->profileRecoverPasswordFinish;
} elsif ($type eq 'email') {
$self->emailRecoverPasswordFinish;
}
}
#-------------------------------------------------------------------
sub profileRecoverPasswordFinish {
my $self = shift;
my $i18n = WebGUI::International->new($self->session);
my $i18n2 = WebGUI::International->new($self->session, 'AuthWebGUI');
return $self->displayLogin unless ($self->session->setting->get('webguiPasswordRecovery') ne '') and $self->userId eq '1';
my $username;
if ($self->getSetting('passwordRecoveryRequireUsername')) {
$username = $self->session->form->process('authWebGUI.username');
return $self->recoverPassword($i18n2->get('password recovery no username')) unless defined $username;
}
@ -742,6 +895,146 @@ sub recoverPasswordFinish {
}
}
#-------------------------------------------------------------------
sub emailRecoverPasswordFinish {
my $self = shift;
return $self->displayLogin unless ($self->session->setting->get('webguiPasswordRecovery') ne '') and $self->userId eq '1';
my $i18n = WebGUI::International->new($self->session);
my $session = $self->session;
my ($form) = $session->quick(qw/form/);
my $email = $form->param('email');
my $username = $form->param('username');
my $user;
# get user from email
$user = WebGUI::User->newByEmail($session, $email) if $email;
# get user from username
if ($username) {
$user = WebGUI::User->newByUsername($session, $username) unless $user;
}
# return error unless we get a valid user.
unless ($user) {
return $i18n->get('recover password not found', 'AuthWebGUI');
}
# generate information necessry to proceed
my $recoveryGuid = $session->id->generate();
my $url = $session->url->getSiteURL;
my $userId = $user->userId; #get the user guid
$email = $user->profileField('email') unless $email; #get email address from the profile, unless we already have it
my $authsettings = $self->getParams;
$authsettings->{emailRecoverPasswordVerificationNumber} = $recoveryGuid;
$self->saveParams($userId, 'WebGUI', $authsettings);
my $mail = WebGUI::Mail::Send->create($session, { to=>$email, subject=>'WebGUI password recovery'});
$mail->addText($i18n->get('recover password email text1', 'AuthWebGUI') . $url. ". \n\n".$i18n->get('recover password email text2', 'AuthWebGUI')." \n\n ".$url."?op=auth;method=emailResetPassword;token=$recoveryGuid"."\n\n ". $i18n->get('recover password email text3', 'AuthWebGUI'));
$mail->send;
return "<h1>". $i18n->get('recover password banner', 'AuthWebGUI')." </h1> <br> <br> <h3>". $i18n->get('email recover password finish message1', 'AuthWebGUI'). $email . $i18n->get('email recover password finish message2', 'AuthWebGUI') . "</h3>";
}
#-------------------------------------------------------------------
# handler for the link generated and mailed by emailRecoverPasswordFinish
sub emailResetPassword {
my $self = shift;
my $errormsg = shift;
my $session = $self->session;
my ($form) = $session->quick(qw/form/);
my $passwordRecoveryToken = $form->param('token');
my $i18n = WebGUI::International->new($self->session);
my $userId = $self->getUserIdByPasswordRecoveryToken($session, $passwordRecoveryToken);
my $u = $self->user(WebGUI::User->new($self->session, $userId));
$self->session->user({user=>$u});
# do not proceed unless we have an incoming guid from the email, and that guid corresponds to a valid user.
unless ($passwordRecoveryToken && $userId) {
return $session->privilege->insufficient;
}
# login the user and take them to a page where they can change their password.
my $output = "<h1>".$i18n->get('recover password banner', 'AuthWebGUI') ."</h1> <br><br><h3>". $i18n->get('email password recovery end message', 'AuthWebGUI')."</h3>";
$output .= $errormsg if $errormsg;
my $f = WebGUI::HTMLForm->new($self->session);
$f->hidden(
name => 'op',
value => 'auth',
);
$f->hidden(
name => "method",
value => "emailResetPasswordFinish",
);
$f->hidden(
name => "token",
value => "$passwordRecoveryToken",
);
$f->password(
name=>"newpassword",
label=> $i18n->get('new password label', 'AuthWebGUI'),
hoverHelp=> $i18n->get('new password help', 'AuthWebGUI'),
);
$f->password(
name=>"newpwdverify",
label => $i18n->get('new password verify', 'AuthWebGUI'),
hoverHelp=> $i18n->get('new password verify help', 'AuthWebGUI'),
);
$f->submit(
value => 'submit'
);
$output .= $f->print;
return $output;
}
#-------------------------------------------------------------------
sub emailResetPasswordFinish {
my $self = shift;
my $session = $self->session;
my ($form) = $session->quick(qw/form/);
my $password = $form->param('newpassword');
my $passwordConfirm = $form->param('newpwdverify');
my $passwordRecoveryToken = $form->param('token');
my $userId = $self->getUserIdByPasswordRecoveryToken($session, $passwordRecoveryToken);
return $session->privilege->insufficient unless $userId;
if ($self->_isValidPassword($password, $passwordConfirm)) {
$self->user(WebGUI::User->new($self->session, $userId));
$self->saveParams($userId, $self->authMethod,
{ identifier => Digest::MD5::md5_base64($password),
passwordLastUpdated => $self->session->datetime->time });
$self->_logSecurityMessage;
# delete the emailRecoverPasswordVerificationNumber
$self->deleteSingleParam($userId, $self->authMethod, 'emailRecoverPasswordVerificationNumber');
return $self->SUPER::login;
} else {
return $self->emailResetPassword($self->error);
}
}
#-------------------------------------------------------------------
sub resetExpiredPassword {
my $self = shift;

View file

@ -440,47 +440,84 @@ sub definition {
#-------------------------------------------------------------------
=head2 www_editSettings ( $session )
=head2 www_editSettings ( $session, $argsHash )
Display a form for sitewide settings, if the user is in group Admin (3).
argsHash is a hash reference of additional arguments to this sub. Available
keys:
errors - An array reference of errors with processing the site settings
NOTE: Must be i18n BEFORE given to this sub
message - A benign message to the user
NOTE: Must be i18n BEFORE given to this sub
=cut
sub www_editSettings {
my $session = shift;
my $session = shift;
my $argsHash = shift;
return $session->privilege->adminOnly() unless ($session->user->isInGroup(3));
my $i18n = WebGUI::International->new($session, "WebGUI");
my %tabs;
tie %tabs, 'Tie::IxHash';
%tabs = (
company=>{ label=>$i18n->get("company") },
content=>{ label=>$i18n->get("content") },
ui=>{ label=>$i18n->get("ui") },
messaging=>{ label=>$i18n->get("messaging") },
misc=>{ label=>$i18n->get("misc") },
user=>{ label=>$i18n->get("user") },
auth=>{ label=>$i18n->get("authentication") },
);
my $i18n = WebGUI::International->new($session, "WebGUI");
my $output = '';
# Show any errors or message
if ($argsHash->{message}) {
$output .= '<p>' . $argsHash->{message} . '</p>';
}
my @errors = @{ $argsHash->{errors} };
if (@errors) {
$output .= '<p>' . $i18n->get("editSettings error occurred") . '</p>'
. '<ul>'
;
for my $error (@errors) {
$output .= "<li>$error</li>";
}
$output .= '</ul>';
}
# Available tabs
# TODO: Build this from the definition instead.
tie my %tabs, 'Tie::IxHash', (
company => { label => $i18n->get("company") },
content => { label => $i18n->get("content") },
ui => { label => $i18n->get("ui") },
messaging => { label => $i18n->get("messaging") },
misc => { label => $i18n->get("misc") },
user => { label => $i18n->get("user") },
auth => { label => $i18n->get("authentication") },
);
# Start the form
my $tabform = WebGUI::TabForm->new($session,\%tabs);
$tabform->hidden({
name=>"op",
value=>"saveSettings"});
name => "op",
value => "saveSettings"
});
my $definitions = definition($session, $i18n);
foreach my $definition (@{$definitions}) {
$tabform->getTab($definition->{tab})->dynamicField(%{$definition});
}
# Get fieldsets for avaiable auth methods
foreach (@{$session->config->get("authMethods")}) {
$tabform->getTab("auth")->fieldSetStart($_);
my $authInstance = WebGUI::Operation::Auth::getInstance($session,$_,1);
$tabform->getTab("auth")->raw($authInstance->editUserSettingsForm);
$tabform->getTab("auth")->fieldSetEnd;
}
$tabform->submit();
$output .= $tabform->print;
my $ac = WebGUI::AdminConsole->new($session,"settings");
$ac->setHelp("settings");
return $ac->render($tabform->print);
return $ac->render($output);
}
#----------------------------------------------------------------------------
=head2 www_saveSettings ( $session )
Form postprocessor for www_editSettings. Returns adminOnly() unless the user
@ -488,22 +525,29 @@ is in group Admin (3). Returns the user to the Edit Settings screen, www_editSe
=cut
#-------------------------------------------------------------------
sub www_saveSettings {
my $session = shift;
my $session = shift;
return $session->privilege->adminOnly() unless ($session->user->isInGroup(3));
my $i18n = WebGUI::International->new($session, "WebGUI");
my $i18n = WebGUI::International->new($session, "WebGUI");
my $setting = $session->setting;
my $form = $session->form;
my @errors; # Errors trying to save the form
my $definitions = definition($session, $i18n);
my $setting = $session->setting;
my $form = $session->form;
foreach my $definition (@{$definitions}) {
$setting->set($definition->{name}, $form->process($definition->{name}, $definition->{fieldType}, undef, $definition));
}
foreach (@{$session->config->get("authMethods")}) {
my $authInstance = WebGUI::Operation::Auth::getInstance($session,$_,1);
$authInstance->editUserSettingsFormSave;
my $authInstance = WebGUI::Operation::Auth::getInstance($session,$_,1);
my $authErrors = $authInstance->editUserSettingsFormSave;
if ($authErrors) {
push @errors, @{ $authErrors };
}
}
return www_editSettings($session);
return www_editSettings($session, { errors => \@errors, message => $i18n->get("editSettings done") });
}
1;

View file

@ -573,6 +573,113 @@ our $I18N = {
message => q{Number of upper-case characters required in password},
lastUpdated => 0,
},
'password recovery email label' => {
message => q|Email Address|,
lastUpdated => 1177127324,
},
'password recovery email hoverHelp' => {
message => q|Enter your login name|,
lastUpdated => 1177127324,
},
'password recovery login label' => {
message => q|Login Name|,
lastUpdated => 1177127324,
},
'password recovery login hoverHelp' => {
message => q|Enter your email address here|,
lastUpdated => 1177127324,
},
'new password label' => {
message => q|New Password|,
lastUpdated => 1177127324,
},
'new password help' => {
message => q|Enter your new password here|,
lastUpdated => 1177127324,
},
'new password verify' => {
message => q|Verify New Password|,
lastUpdated => 1177127324,
},
'new password verify help' => {
message => q|Enter your password again to verify|,
lastUpdated => 177127324,
},
'recover password not found' => {
message => q|We have no record of a user matching the information you have given|,
lastUpdated => 177127324,
},
'recover password email text1' => {
message => q|We have received your request the password for |,
lastUpdated => 177127324,
},
'recover password email text2' => {
message => q|Pleae use the link below to visit the site and change your password|,
lastUpdated => 177127324,
},
'recover password email text3' => {
message => q|If you did not request your password to be recovered, please contact the system administrator by replying to this message|,
lastUpdated => 177127324,
},
'recover password banner' => {
message => q|Password Recovery|,
lastUpdated => 177127324,
},
'email recover password finish message1' => {
message => q|An email has been sent to |,
lastUpdated => 177127324,
},
'email recover password finish message2' => {
message => q| with instructions for resetting your password.|,
lastUpdated => 177127324,
},
'email recover password start message' => {
message => q|Enter either your email address or your login below to initiate the passwrod recovery process.|,
lastUpdated => 177127324,
},
'email password recovery end message' => {
message => q|Enter a new password for your account below.|,
lastUpdated => 177127324,
},
'setting passwordRecoveryType profile' => {
message => "Profile field",
lastUpdated => 0,
},
'setting passwordRecoveryType email' => {
message => "E-mail address",
lastUpdated => 0,
},
'setting passwordRecoveryType none' => {
message => "No",
lastUpdated => 0,
},
'error passwordRecoveryType no profile fields required' => {
message => q{Cannot enable WebGUI authentication password recovery
by profile field: There are no user profile fields required for password recovery.},
lastUpdated => 0,
},
};
1;

View file

@ -4251,6 +4251,16 @@ Get a copy of wget and use this: <code>wget -p -r --html-extension -k http://the
message => q|Choose a template for sending private messages|,
lastUpdated => 1181019679,
},
'editSettings error occurred' => {
message => q{The following errors occurred while trying to save settings.},
lastUpdated => 0,
},
'editSettings done' => {
message => "Settings saved!",
lastUpdated => 0,
},
};