Overhaul password changing

This commit is contained in:
Chris 2017-01-02 12:13:38 +00:00
parent 11c2a6ecf8
commit 7ea4f2215a
4 changed files with 236 additions and 147 deletions

View File

@ -30,6 +30,44 @@ use LWP::UserAgent;
use JSON; use JSON;
use v5.14; use v5.14;
# ============================================================================
# Utility/support functions
## @method private $ _build_password_policy()
# Build a string describing the password policy for the current user. This
# interrogates the user's AuthMethod to determine the password policy in place
# for the user (if any), and generates a string describing it in a format
# suitable to present to users.
#
# @return A string containing the password policy enforced for the logged-in
# user.
sub _build_password_policy {
my $self = shift;
# Anonymous user can have no policy
return '{L_LOGIN_PASSCHANGE_ERRNOUSER}'
if($self -> {"session"} -> anonymous_session());
my $user = $self -> {"session"} -> get_user_byid()
or return '{L_LOGIN_PASSCHANGE_ERRNOUSER}';
# Fetch the policy, and give up if there isn't one.
my $policy = $self -> {"session"} -> {"auth"} -> get_policy($user -> {"username"})
or return '{L_LOGIN_POLICY_NONE}';
my $policystr = "";
foreach my $name (@{$policy -> {"policy_order"}}) {
next if(!$policy -> {$name});
$policystr .= $self -> {"template"} -> load_template("login/policy.tem",
{ "%(policy)s" => "{L_LOGIN_".uc($name)."}",
"%(value)s" => $policy -> {$name} });
}
return $policystr;
}
# ============================================================================ # ============================================================================
# Emailer functions # Emailer functions
@ -306,18 +344,27 @@ sub _validate_signin {
} }
## @method private $ _validate_recaptcha($response) ## @method private $ _validate_recaptcha()
# Given a response code submitted as part of an operation by the user, ask the # Pull the reCAPTCHA response string from the posted data, and ask the
# google reCAPTCHA validation service to check whether the response is valid. # google reCAPTCHA validation service to check whether the response is valid.
# #
# @param response The reCAPTCHA response code submitted by the user # @return true if the code is valid, undef if not - examine $self -> errstr()
# @return true if the code is valid, false if is not, undef on error. # for the reason why in this case
sub _validate_recaptcha { sub _validate_recaptcha {
my $self = shift; my $self = shift;
my $response = shift;
$self -> clear_error(); $self -> clear_error();
# Pull the reCAPTCHA response code
my ($response, $error) = $self -> validate_string("g-recaptcha-response", {"required" => 1,
"nicename" => "{L_LOGIN_RECAPTCHA}",
"minlen" => 2,
"formattest" => '^[-\w]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECAPTCHA}"
});
return $self -> self_error($error)
if($error);
my $ua = LWP::UserAgent -> new(); my $ua = LWP::UserAgent -> new();
my $data = { my $data = {
@ -341,7 +388,7 @@ sub _validate_recaptcha {
return $self -> self_error("HTTP problem: ".$resp -> status_line()); return $self -> self_error("HTTP problem: ".$resp -> status_line());
} }
return 0; return $self -> self_error("{L_LOGIN_ERR_RECAPTCHA}");
} }
@ -407,23 +454,10 @@ sub _validate_signup {
if($user); if($user);
} }
# Pull the reCAPTCHA response code # Is the reCAPTCHA response valid?
($args -> {"recaptcha"}, $error) = $self -> validate_string("g-recaptcha-response", {"required" => 1, return ($self -> {"template"} -> load_template("error/error_list.tem", { "%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"nicename" => "{L_LOGIN_RECAPTCHA}", "%(errors)s" => $self -> errstr() }), $args)
"minlen" => 2, unless($self -> _validate_recaptcha());
"formattest" => '^[-\w]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECAPTCHA}"
});
# Halt here if there are any problems.
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => $error}), $args)
if($error);
# Is the response valid?
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => "{L_LOGIN_ERR_RECAPTCHA}"}), $args)
unless($self -> _validate_recaptcha($args -> {"recaptcha"}));
# Get here an the user's details are okay, register the new user. # Get here an the user's details are okay, register the new user.
@ -498,23 +532,9 @@ sub _validate_resend {
my $error; my $error;
# Get the recaptcha check out of the way first # Get the recaptcha check out of the way first
# Pull the reCAPTCHA response code return ($self -> {"template"} -> load_template("error/error_list.tem", { "%(message)s" => "{L_LOGIN_RESEND_FAILED}",
($args -> {"recaptcha"}, $error) = $self -> validate_string("g-recaptcha-response", {"required" => 1, "%(errors)s" => $self -> errstr() }), $args)
"nicename" => "{L_LOGIN_RECAPTCHA}", unless($self -> _validate_recaptcha());
"minlen" => 2,
"formattest" => '^[-\w]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECAPTCHA}"
});
# Halt here if there are any problems.
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => $error}), $args)
if($error);
# Is the response valid?
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => "{L_LOGIN_ERR_RECAPTCHA}"}), $args)
unless($self -> _validate_recaptcha($args -> {"recaptcha"}));
# Get the email address entered by the user # Get the email address entered by the user
@ -576,23 +596,9 @@ sub _validate_recover {
my $error; my $error;
# Get the recaptcha check out of the way first # Get the recaptcha check out of the way first
# Pull the reCAPTCHA response code return ($self -> {"template"} -> load_template("error/error_list.tem", { "%(message)s" => "{L_LOGIN_RECOVER_FAILED}",
($args -> {"recaptcha"}, $error) = $self -> validate_string("g-recaptcha-response", {"required" => 1, "%(errors)s" => $self -> errstr() }), $args)
"nicename" => "{L_LOGIN_RECAPTCHA}", unless($self -> _validate_recaptcha());
"minlen" => 2,
"formattest" => '^[-\w]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECAPTCHA}"
});
# Halt here if there are any problems.
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => $error}), $args)
if($error);
# Is the response valid?
return ($self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_LOGIN_ERR_REGFAILED}",
"%(errors)s" => "{L_LOGIN_ERR_RECAPTCHA}"}), $args)
unless($self -> _validate_recaptcha($args -> {"recaptcha"}));
# Get the email address entered by the user # Get the email address entered by the user
($args -> {"email"}, $error) = $self -> validate_string("email", {"required" => 1, ($args -> {"email"}, $error) = $self -> validate_string("email", {"required" => 1,
@ -638,6 +644,67 @@ sub _validate_recover {
} }
## @method private @ validate_reset()
# Pull the userid and activation code out of the submitted data, and determine
# whether they are valid (and that the user's authmethod allows for resets). If
# so, reset the user's password and send and email to them with the new details.
#
# @return Two values: a reference to the user whose password has been reset
# on success, or an error message, and a reference to a hash containing
# the data entered by the user.
sub _validate_reset {
my $self = shift;
my $args = {};
my $error;
# Obtain the userid from the query string, if possible.
($args -> {"uid"}, $error) = $self -> validate_numeric("uid", { "required" => 1,
"nidename" => "{L_LOGIN_UID}",
"intonly" => 1,
"min" => 2});
return ("{L_LOGIN_ERR_NOUID}", $args)
if($error);
my $user = $self -> {"session"} -> {"auth"} -> {"app"} -> get_user_byid($args -> {"uid"})
or return ("{L_LOGIN_ERR_BADUID}", $args);
# Get the reset code, should be a 64 character alphanumeric string
($args -> {"resetcode"}, $error) = $self -> validate_string("resetcode", {"required" => 1,
"nicename" => "{L_LOGIN_RESET_CODE}",
"minlen" => 64,
"maxlen" => 64,
"formattest" => '^[a-zA-Z0-9]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECCHAR}"});
return ($error, $args) if($error);
# Does the reset code match the one set for the user?
return ("{L_LOGIN_ERR_BADRECCODE}", $args)
unless($user -> {"act_code"} && $user -> {"act_code"} eq $args -> {"resetcode"});
# Users can not recover an inactive account - they need to get a new act code
return ("{L_LOGIN_ERR_NORECINACT}", $args)
if($self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "activate") &&
!$self -> {"session"} -> {"auth"} -> activated($user -> {"username"}));
# double-check the authmethod supports resets, just to be on the safe side (the code should never
# get here if it does not, but better safe than sorry)
return ($self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "recover_message"), $args)
if(!$self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "recover"));
# Okay, user is valid, authcode checks out, auth module supports resets, generate a new
# password and send it
my $newpass = $self -> {"session"} -> {"auth"} -> reset_password($user -> {"username"});
return ($self -> {"template"} -> load_template("error/error.tem", { "%(message)s" => "{L_LOGIN_RECOVER_FAILED}",
"%(reason)s" => $self -> {"session"} -> {"auth"} -> errstr()}), $args)
if(!$newpass);
# Get here and the user's account has been reset
$self -> _reset_email($user, $newpass);
return($user, $args);
}
## @method private @ validate_passchange() ## @method private @ validate_passchange()
# Determine whether the password change request made by the user is valid. If the # Determine whether the password change request made by the user is valid. If the
# password change is valid (passwords match, pass policy, and the old password is # password change is valid (passwords match, pass policy, and the old password is
@ -646,13 +713,17 @@ sub _validate_recover {
# @return An array of two values: A reference to the user's data on success, # @return An array of two values: A reference to the user's data on success,
# or an error string if the change failed, and a reference to a hash of # or an error string if the change failed, and a reference to a hash of
# arguments that passed validation. # arguments that passed validation.
# FIXME: OVERHAUL
sub _validate_passchange { sub _validate_passchange {
my $self = shift; my $self = shift;
my $error = ""; my $error = "";
my $errors = ""; my $errors = "";
my $args = {}; my $args = {};
# Get the recaptcha check out of the way first
return ($self -> {"template"} -> load_template("error/error_list.tem", { "%(message)s" => "{L_LOGIN_PASSCHANGE_FAILED}",
"%(errors)s" => $self -> errstr() }), $args)
unless($self -> _validate_recaptcha());
# Need to get a logged-in user before anything else is done # Need to get a logged-in user before anything else is done
my $user = $self -> {"session"} -> get_user_byid(); my $user = $self -> {"session"} -> get_user_byid();
return ($self -> {"template"} -> load_template("error/error_list.tem", return ($self -> {"template"} -> load_template("error/error_list.tem",
@ -738,67 +809,6 @@ sub _validate_passchange {
} }
## @method private @ validate_reset()
# Pull the userid and activation code out of the submitted data, and determine
# whether they are valid (and that the user's authmethod allows for resets). If
# so, reset the user's password and send and email to them with the new details.
#
# @return Two values: a reference to the user whose password has been reset
# on success, or an error message, and a reference to a hash containing
# the data entered by the user.
sub _validate_reset {
my $self = shift;
my $args = {};
my $error;
# Obtain the userid from the query string, if possible.
($args -> {"uid"}, $error) = $self -> validate_numeric("uid", { "required" => 1,
"nidename" => "{L_LOGIN_UID}",
"intonly" => 1,
"min" => 2});
return ("{L_LOGIN_ERR_NOUID}", $args)
if($error);
my $user = $self -> {"session"} -> {"auth"} -> {"app"} -> get_user_byid($args -> {"uid"})
or return ("{L_LOGIN_ERR_BADUID}", $args);
# Get the reset code, should be a 64 character alphanumeric string
($args -> {"resetcode"}, $error) = $self -> validate_string("resetcode", {"required" => 1,
"nicename" => "{L_LOGIN_RESET_CODE}",
"minlen" => 64,
"maxlen" => 64,
"formattest" => '^[a-zA-Z0-9]+$',
"formatdesc" => "{L_LOGIN_ERR_BADRECCHAR}"});
return ($error, $args) if($error);
# Does the reset code match the one set for the user?
return ("{L_LOGIN_ERR_BADRECCODE}", $args)
unless($user -> {"act_code"} && $user -> {"act_code"} eq $args -> {"resetcode"});
# Users can not recover an inactive account - they need to get a new act code
return ("{L_LOGIN_ERR_NORECINACT}", $args)
if($self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "activate") &&
!$self -> {"session"} -> {"auth"} -> activated($user -> {"username"}));
# double-check the authmethod supports resets, just to be on the safe side (the code should never
# get here if it does not, but better safe than sorry)
return ($self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "recover_message"), $args)
if(!$self -> {"session"} -> {"auth"} -> capabilities($user -> {"username"}, "recover"));
# Okay, user is valid, authcode checks out, auth module supports resets, generate a new
# password and send it
my $newpass = $self -> {"session"} -> {"auth"} -> reset_password($user -> {"username"});
return ($self -> {"template"} -> load_template("error/error.tem", { "%(message)s" => "{L_LOGIN_RECOVER_FAILED}",
"%(reason)s" => $self -> {"session"} -> {"auth"} -> errstr()}), $args)
if(!$newpass);
# Get here and the user's account has been reset
$self -> _reset_email($user, $newpass);
return($user, $args);
}
# ============================================================================ # ============================================================================
# Form generators # Form generators
@ -939,39 +949,45 @@ sub _generate_recover_form {
# @param error A string containing errors related to password changes, or undef. # @param error A string containing errors related to password changes, or undef.
# @return An array containing the page title, content, extra header data, and # @return An array containing the page title, content, extra header data, and
# extra javascript content. # extra javascript content.
# FIXME: OVERHAUL
sub _generate_passchange_form { sub _generate_passchange_form {
my $self = shift; my $self = shift;
my $error = shift; my $error = shift;
my $reasons = { 'temporary' => "{L_LOGIN_FORCECHANGE_TEMP}", my $reasons = {
'expired' => "{L_LOGIN_FORCECHANGE_OLD}", }; 'temporary' => "{L_LOGIN_PASSCHANGE_TEMP}",
'expired' => "{L_LOGIN_PASSCHANGE_OLD}",
'manual' => "{L_LOGIN_PASSCHANGE_MANUAL}"
};
# convert the password policy to a string # convert the password policy to a string
my $policy = $self -> build_password_policy("login/policy.tem") || "{L_LOGIN_POLICY_NONE}"; my $policy = $self -> _build_password_policy();
# Reason should be in the 'passchange_reason' session variable. # Reason should be in the 'passchange_reason' session variable.
my $reason = $self -> {"session"} -> get_variable("passchange_reason", "temporary"); my $reason = $self -> {"session"} -> get_variable("passchange_reason", "manual");
# Force a sane reason # Force a sane reason
$reason = 'temporary' unless($reason && $reasons -> {$reason}); $reason = 'manual' unless($reason && $reasons -> {$reason});
# Wrap the error message in a message box if we have one. # Wrap the error message in a message box if we have one.
$error = $self -> {"template"} -> load_template("error/error_box.tem", {"%(message)s" => $error}) $error = $self -> {"template"} -> load_template("error/error_box.tem", {"%(message)s" => $error})
if($error); if($error);
return ("{L_LOGIN_TITLE}", return ("{L_LOGIN_PASSCHANGE_TITLE}",
$self -> {"template"} -> load_template("login/force_password.tem", {"%(error)s" => $error, $self -> {"template"} -> load_template("login/form_passchange.tem",
"%(target)s" => $self -> build_url("block" => "login"), { "%(error)s" => $error,
"%(policy)s" => $policy, "%(sitekey)s" => $self -> {"settings"} -> {"config"} -> {"Login:recaptcha_sitekey"},
"%(reason)s" => $reasons -> {$reason}, "%(url-target)s" => $self -> build_url(block => "login", pathinfo => [ "passchange" ]),
"%(rid)s" => $reason } )); "%(policy)s" => $policy,
"%(reason)s" => $reasons -> {$reason},
"%(rid)s" => $reason } ),
$self -> {"template"} -> load_template("login/signup_extrahead.tem"),
$self -> {"template"} -> load_template("login/extrajs.tem"));
} }
# ============================================================================ # ============================================================================
# Response generators # Response generators
## @method private @ generate_loggedin() ## @method private @ _generate_loggedin()
# Generate the contents of a page telling the user that they have successfully logged in. # Generate the contents of a page telling the user that they have successfully logged in.
# #
# @return An array of two values: the page title string, and the 'logged in' message. # @return An array of two values: the page title string, and the 'logged in' message.
@ -1013,7 +1029,7 @@ sub _generate_loggedin {
} }
## @method private @ generate_signedout() ## @method private @ _generate_signedout()
# Generate the contents of a page telling the user that they have successfully logged out. # Generate the contents of a page telling the user that they have successfully logged out.
# #
# @return An array of two values: the page title string, and the 'logged out' message # @return An array of two values: the page title string, and the 'logged out' message
@ -1036,7 +1052,7 @@ sub _generate_signedout {
} }
## @method private @ generate_activated($user) ## @method private @ _generate_activated($user)
# Generate the contents of a page telling the user that they have successfully activated # Generate the contents of a page telling the user that they have successfully activated
# their account. # their account.
# #
@ -1059,7 +1075,7 @@ sub _generate_activated {
} }
## @method private @ generate_signedup() ## @method private @ _generate_signedup()
# Generate the contents of a page telling the user that they have successfully created an # Generate the contents of a page telling the user that they have successfully created an
# inactive account. # inactive account.
# #
@ -1081,7 +1097,7 @@ sub _generate_signedup {
} }
## @method private @ generate_resent() ## @method private @ _generate_resent()
# Generate the contents of a page telling the user that a new activation code has been # Generate the contents of a page telling the user that a new activation code has been
# sent to their email address. # sent to their email address.
# #
@ -1103,7 +1119,7 @@ sub _generate_resent {
} }
## @method private @ generate_recover() ## @method private @ _generate_recover()
# Generate the contents of a page telling the user that a new password has been # Generate the contents of a page telling the user that a new password has been
# sent to their email address. # sent to their email address.
# #
@ -1124,7 +1140,7 @@ sub _generate_recover {
} }
## @method private @ generate_reset() ## @method private @ _generate_reset()
# Generate the contents of a page telling the user that a new password has been # Generate the contents of a page telling the user that a new password has been
# sent to their email address. # sent to their email address.
# #
@ -1158,6 +1174,28 @@ sub _generate_reset {
} }
## @method private @ _generate_passchanged()
# Generate the contents of a page telling the user that they have successfully created an
# inactive account.
#
# @return An array of two values: the page title string, the 'registered' message.
sub _generate_passchanged {
my $self = shift;
my $url = $self -> build_url(block => "login",
pathinfo => [ ]);
return ("{L_LOGIN_PASSCHANGE_DONETITLE}",
$self -> message_box(title => "{L_LOGIN_PASSCHANGE_DONETITLE}",
type => "account",
summary => "{L_LOGIN_PASSCHANGE_SUMMARY}",
message => "{L_LOGIN_PASSCHANGE_MESSAGE}",
buttons => [ {"message" => "{L_SITE_CONTINUE}",
"colour" => "standard",
"href" => $url} ]));
}
# ============================================================================ # ============================================================================
# API handling # API handling
@ -1338,23 +1376,28 @@ sub _handle_reset {
} }
# FIXME: OVERHAUL ## @method private @ _handle_passchange()
# Handle the process of showing the form they can change their password
# through, and processing submission from the form.
#
# @return An array containing the page title, content, extra header data, and
# extra javascript content.
sub _handle_passchange { sub _handle_passchange {
my $self = shift; my $self = shift;
if(defined($self -> {"cgi"} -> param("changepass"))) { if(defined($self -> {"cgi"} -> param("changepass"))) {
# Check the password is valid- # Check the password form is valid
my ($user, $args) = $self -> validate_passchange(); my ($user, $args) = $self -> _validate_passchange();
# Change failed, send back the change form # Change failed, send back the change form
if(!ref($user)) { if(!ref($user)) {
$self -> log("passchange error", $user); $self -> log("passchange error", $user);
return $self -> _generate_passchange_form($user); return $self -> _generate_passchange_form($user);
# Change done, send back the loggedin page # Change done, send back the loggedin page
} else { } else {
$self -> log("password updated", $user); $self -> log("password updated", $user);
return $self -> _generate_loggedin(); return $self -> _generate_passchanged();
} }
} }

View File

@ -128,10 +128,11 @@ LOGIN_RESEND_SUMMARY = Resend successful!
LOGIN_RESEND_MESSAGE = A new password and an activation link have been send to your email address.<br /><br />Please check your email for a message with the subject 'Your {V_[sitename]} activation code' and follow the instructions it contains to activate your account. LOGIN_RESEND_MESSAGE = A new password and an activation link have been send to your email address.<br /><br />Please check your email for a message with the subject 'Your {V_[sitename]} activation code' and follow the instructions it contains to activate your account.
# Force password change # Force password change
LOGIN_PASSCHANGE = Change password LOGIN_PASSCHANGE_TITLE = Change password
LOGIN_FORCECHANGE_INTRO = Before you continue, please choose a new password to set for your account. LOGIN_PASSCHANGE_INTRO = Please choose a new password using the form below.
LOGIN_FORCECHANGE_TEMP = Your account is currently set up with a temporary password. LOGIN_PASSCHANGE_TEMP = Your account is currently set up with a temporary password.
LOGIN_FORCECHANGE_OLD = The password on your account has expired as a result of age limits enforced by the site's password policy. LOGIN_PASSCHANGE_OLD = The password on your account has expired as a result of age limits enforced by the site's password policy.
LOGIN_PASSCHANGE_MANUAL = You have chosen to change the password set for your account.
LOGIN_NEWPASSWORD = New password LOGIN_NEWPASSWORD = New password
LOGIN_CONFPASS = Confirm password LOGIN_CONFPASS = Confirm password
LOGIN_OLDPASS = Your current password LOGIN_OLDPASS = Your current password
@ -143,6 +144,10 @@ LOGIN_PASSCHANGE_ERRMATCH = The new password specified does not match the confi
LOGIN_PASSCHANGE_ERRSAME = The new password can not be the same as the old password. LOGIN_PASSCHANGE_ERRSAME = The new password can not be the same as the old password.
LOGIN_PASSCHANGE_ERRVALID = The specified old password is not correct. You must enter the password you used to sign in. LOGIN_PASSCHANGE_ERRVALID = The specified old password is not correct. You must enter the password you used to sign in.
LOGIN_PASSCHANGE_DONETITLE = Password changed
LOGIN_PASSCHANGE_SUMMARY = Password change successful!
LOGIN_PASSCHANGE_MESSAGE = Your password has been updated successfully. The next time you log in you should enter your new password.
LOGIN_POLICY = Password policy LOGIN_POLICY = Password policy
LOGIN_POLICY_INTRO = When choosing a new password, keep in mind that: LOGIN_POLICY_INTRO = When choosing a new password, keep in mind that:
LOGIN_POLICY_NONE = No password policy is currently in place, you may use any password you want. LOGIN_POLICY_NONE = No password policy is currently in place, you may use any password you want.

View File

@ -0,0 +1,40 @@
<div class="small-8 small-centered columns">
%(error)s
<!-- Start passchange form -->
<div class="passchangeform callout secondary">
<div>%(reason)s {L_LOGIN_PASSCHANGE_INTRO}</div>
<form id="signinform" method="post" action="%(url-target)s">
<div class="entry">
<label for="newpass">{L_LOGIN_NEWPASSWORD}:<br />
<input type="password" id="newpass" name="newpass" size="24" maxlength="255" />
</label>
</div>
<div class="entry">
<label for="confirm">{L_LOGIN_CONFPASS}:<br />
<input type="password" id="confirm" name="confirm" size="24" maxlength="255" />
</label>
</div>
<hr />
<div class="entry">
<label for="oldpass">{L_LOGIN_OLDPASS}:<br />
<input type="password" id="oldpass" name="oldpass" size="24" maxlength="255" />
</label>
</div>
<div class="g-recaptcha" data-sitekey="%(sitekey)s"></div>
<div class="submit clearfix">
<input type="submit" class="button float-right nomargin" id="changepass" name="changepass" value="{L_LOGIN_SETPASS}" />
</div>
</form>
</div>
<!-- End passchange form -->
</div>
</div>
<div class="row" id="policy">
<div class="small-8 small-centered columns">
<div class="policy callout secondary">
<div>{L_LOGIN_POLICY_INTRO}</div>
<ul>
%(policy)s
</ul>
</div>
</div>

View File

@ -0,0 +1 @@
<li>%(policy)s</li>