aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain d'Alverny <rda@mageia.org>2011-01-07 23:20:14 +0000
committerRomain d'Alverny <rda@mageia.org>2011-01-07 23:20:14 +0000
commit5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef (patch)
tree48c83c63482380e25f93b544be518ecc6355bb3b
parentf07e6b1361c4817980888496a14b9b8d1fd5578d (diff)
downloadidentity-5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef.tar
identity-5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef.tar.gz
identity-5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef.tar.bz2
identity-5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef.tar.xz
identity-5f8c9ba5f32c0dab7752a7c50dab5b82639f81ef.zip
commit implement forgot_password patch from Maarten Vanraes
-rw-r--r--catdap.yml7
-rw-r--r--lib/CatDap/Controller/forgot_password.pm270
-rw-r--r--root/email/forgot_password.tt7
-rw-r--r--root/forgot_password/check.tt4
-rw-r--r--root/forgot_password/complete.tt6
-rw-r--r--root/forgot_password/confirm.tt21
-rw-r--r--root/forgot_password/index.tt17
-rw-r--r--root/index.tt3
8 files changed, 333 insertions, 2 deletions
diff --git a/catdap.yml b/catdap.yml
index 88dcfe8..671fb12 100644
--- a/catdap.yml
+++ b/catdap.yml
@@ -35,6 +35,12 @@ register:
login_blacklist:
- apache
+forgot_password:
+ secret:
+ path: '/tmp/'
+ prefix: 'catdap-forgot_password-'
+ timeout: 259200
+
authentication:
default_realm: ldap
realms:
@@ -55,6 +61,7 @@ authentication:
user_filter: '(&(objectClass=inetOrgPerson)(uid=%s))'
user_scope: 'one'
user_field: 'uid'
+ email_filter: '(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlternateAddress=%s)))'
use_roles: 1
role_basedn: 'dc=mageia,dc=org'
role_scope: 'sub'
diff --git a/lib/CatDap/Controller/forgot_password.pm b/lib/CatDap/Controller/forgot_password.pm
new file mode 100644
index 0000000..b5dde29
--- /dev/null
+++ b/lib/CatDap/Controller/forgot_password.pm
@@ -0,0 +1,270 @@
+package CatDap::Controller::forgot_password;
+use Moose;
+use namespace::autoclean;
+use Email::Valid;
+use Data::UUID;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+CatDap::Controller::forgot_password - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) : Form {
+ my ( $self, $c ) = @_;
+
+ if (defined $c->user) {
+ # if we're logged in, we haven't forgotten the password
+ $c->log->debug('Redirecting to /user');
+ $c->res->redirect('/user');
+ }
+}
+
+sub check : Local {
+ my ( $self, $c ) = @_;
+
+ my %details = %{$c->request->params};
+ my $username = lc($c->request->params->{uid});
+ my @errors;
+ $c->stash(errors => []);
+ my $email = $c->request->params->{mail};
+ if (! Email::Valid->address($email)) {
+ push @errors, $c->loc('Invalid email address');
+ }
+
+ if (@errors) {
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+
+ # check in LDAP now that we have validated username and email
+ my $emailfilter = $c->config->{'authentication'}{'realms'}{'ldap'}{'store'}{'email_filter'};
+ $emailfilter =~ s/\%s/$email/g,
+ $c->log->debug("Searching for email $email with filter $emailfilter");
+ my $mesg = $c->model('Proxy')->search($emailfilter);
+ if (!$mesg->entries()) {
+ push @errors,$c->loc(
+ 'This email address is not bound to an account'
+ );
+ }
+
+ if (@errors) {
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+
+ if ($mesg->code) {
+ push @errors,$mesg->error;
+ $c->log->info( sprintf("finding email $email failed: %s", $mesg->error) );
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'register/index.tt');
+ return;
+ }
+
+ my $secret = gen_secret($c, $email);
+
+ $c->stash(
+ email => {
+ 'to' => $email,
+ 'from' => ${$c->config}{'emailfrom'},
+ 'subject' => ${$c->config}{'apptitle'} . " - " . $c->loc('Forgot password'),
+ 'template' => 'forgot_password.tt',
+ },
+ url => $c->uri_for('/forgot_password/confirm') . "?secret=$secret",
+ );
+
+ $c->log->info("Sending forgot password mail to email address $email");
+ $c->forward( $c->view('Email::Template') );
+ if ( @{ $c->error } ) {
+ my $errors = join "\n",@{ $c->error };
+ $c->log->info("Sending activation mail to $email failed: $errors");
+ $c->response->body($c->loc('An error occured sending the email, please try again later. Errors [_1]', $errors));
+ $c->error(0); # Reset the error condition if you need to
+ }
+ $c->stash(template => 'forgot_password/complete.tt');
+}
+
+sub confirm : Local {
+ my ($self, $c) = @_;
+ my $secret = $c->req->param('secret');
+ my @errors;
+
+ # show confirm page which can enter new password
+ if (defined $c->user) {
+ # if we're logged in, we haven't forgotten the password
+ $c->log->debug('Redirecting to /user');
+ $c->res->redirect('/user');
+ }
+
+ # find secret
+ my $email = find_secret($c, $secret);
+ if (!$email) {
+ push @errors, "Secret has expired, please try again.";
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+ my $mesg = find_user_email($c, $email);
+ if ($mesg->code) {
+ push @errors, "Secret has expired, please try again.";
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+
+ # show template to enter a new password
+ $c->stash(secret => $secret, template => 'forgot_password/confirm.tt');
+}
+
+sub change_password : Local {
+ my ($self, $c) = @_;
+ my @errors = ();
+ my $secret = $c->req->param('secret');
+ my $newpass;
+
+ # find secret
+ my $email = find_secret($c, $secret);
+ if (!$email) {
+ push @errors, "Secret has expired, please try again.";
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+ my $mesg = find_user_email($c, $email);
+ if ( $mesg->code) {
+ push @errors, "Secret has expired, please try again.";
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/index.tt');
+ return;
+ }
+ my $entry = $mesg->entry;
+
+ # check if both passwords are equal and are confirm the validation norms
+ if ($c->req->param('newpassword1') eq $c->req->param('newpassword2')) {
+ $newpass = $c->req->param('newpassword1');
+ } else {
+ push @errors, "New passwords dont match";
+ }
+ # if error show confirm page again to retry
+ if (@errors) {
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/confirm.tt');
+ return;
+ }
+
+ # change password
+ my $pp = Net::LDAP::Control::PasswordPolicy->new;
+ $mesg = $c->model('Proxy')->set_password(
+ user => $entry->dn,
+ newpasswd => $newpass,
+ control => [ $pp ],
+ );
+ if ($mesg->code) {
+ my $perror = $mesg->error;
+ push @errors, "Password change failed: $perror";
+ $c->detach;
+ }
+
+ # if error show confirm page again to retry
+ if (@errors) {
+ $c->stash(errors => \@errors);
+ $c->stash(template => 'forgot_password/confirm.tt');
+ return;
+ }
+
+ # TODO: log in by setting the $c->user
+
+ # remove the stored secret
+ remove_secret($c, $secret);
+
+ # redirect to /
+ $c->log->debug('Redirecting to /');
+ $c->res->redirect('/');
+}
+
+sub gen_secret {
+ my ($c, $email) = @_;
+ my $ug = new Data::UUID;
+ # generate a unique secret
+ my $secret = $ug->create_str();
+ my $filename = $c->config->{'forgot_password'}{'secret'}{'path'} .'/'. $c->config->{'forgot_password'}{'secret'}{'prefix'} . $secret;
+ # store secret with email
+ open FILE, ">$filename";
+ print FILE $email;
+ close FILE;
+ return $secret;
+}
+
+sub find_secret {
+ my ($c, $secret) = @_;
+ my $email;
+ my $filename = $c->config->{'forgot_password'}{'secret'}{'path'} .'/'. $c->config->{'forgot_password'}{'secret'}{'prefix'} . $secret;
+ my $timeout = 259200; # 3days in seconds
+ if ($c->config->{'forgot_password'}{'secret'}{'timeout'}) {
+ $timeout = $c->config->{'forgot_password'}{'secret'}{'timeout'};
+ }
+
+ # find secret
+ if (!$secret || !open(FILE, "<$filename")) {
+ # if secret is wrong, timeout expired?
+ return '';
+ }
+ read(FILE, $email, 255);
+ close FILE;
+
+ # check the time, and see if it's longer than timeout
+ my @s = stat($filename);
+ if (time() > $s[9] + $timeout) {
+ # expired
+ return '';
+ }
+
+ return $email;
+}
+
+sub remove_secret {
+ my ($c, $secret) = @_;
+ my $filename = $c->config->{'forgot_password'}{'secret'}{'path'} .'/'. $c->config->{'forgot_password'}{'secret'}{'prefix'} . $secret;
+ unlink $filename;
+}
+
+sub find_user_email {
+ my ($c, $email) = @_;
+
+ # find user by email;
+ my $emailfilter = $c->config->{'authentication'}{'realms'}{'ldap'}{'store'}{'email_filter'};
+ $emailfilter =~ s/\%s/$email/g,
+ $c->log->debug("Searching for email $email with filter $emailfilter");
+ return $c->model('Proxy')->search($emailfilter);
+}
+
+
+=head1 AUTHOR
+
+Buchan Milne
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/root/email/forgot_password.tt b/root/email/forgot_password.tt
new file mode 100644
index 0000000..4826955
--- /dev/null
+++ b/root/email/forgot_password.tt
@@ -0,0 +1,7 @@
+[% l('Dear [_1],',cn) %]
+[% l('Your [_1] account has been requested to change the password. If you did not do this, or you do not want to change your password; you can just do nothing.',c.config.organisation) %]
+[% l('To reset your password, please follow the link below.') %]
+[% url %]
+
+--
+http://mageia.org/
diff --git a/root/forgot_password/check.tt b/root/forgot_password/check.tt
new file mode 100644
index 0000000..b8ec933
--- /dev/null
+++ b/root/forgot_password/check.tt
@@ -0,0 +1,4 @@
+<h2>Success</h2>
+<p>
+[% message %]
+</p> \ No newline at end of file
diff --git a/root/forgot_password/complete.tt b/root/forgot_password/complete.tt
new file mode 100644
index 0000000..3a9995c
--- /dev/null
+++ b/root/forgot_password/complete.tt
@@ -0,0 +1,6 @@
+<h2>[% l('Email sent.') %]</h2>
+
+<p>
+ [% l('Operation was successful.') %]
+ [% l('Check your mail for password reset instructions.') %]
+</p>
diff --git a/root/forgot_password/confirm.tt b/root/forgot_password/confirm.tt
new file mode 100644
index 0000000..3c3e07d
--- /dev/null
+++ b/root/forgot_password/confirm.tt
@@ -0,0 +1,21 @@
+
+<h1>[% l('Enter new password.') %]</h1>
+
+<div id="input_form">
+<form method="post" action="/forgot_password/change_password">
+ <table>
+ <tr>
+ <td><label for="newpassword1_">[% l('New Password') %]</label></td>
+ <td><input id="newpassword1_" name="newpassword1" type="password" value="" /></td>
+ </tr><tr>
+ <td><label for="newpassword2_">[% l('Repeat New Password') %]</label></td>
+ <td><input id="newpassword2_" name="newpassword2" type="password" value="" /></td>
+ </tr><tr>
+ <td colspan="2" style="text-align:center;">
+ <input type="hidden" name="secret" value="[% secret %]" />
+ <button type="submit" value="[% l('Set new password') %]">[% l('Set new password') %]</button>
+ </td>
+ </tr>
+ </table>
+</form>
+</div>
diff --git a/root/forgot_password/index.tt b/root/forgot_password/index.tt
new file mode 100644
index 0000000..c5fbddd
--- /dev/null
+++ b/root/forgot_password/index.tt
@@ -0,0 +1,17 @@
+
+<h1>[% l('Forgot your password?') %]</h1>
+
+<div id="input_form">
+<form method="post" action="/forgot_password/check">
+ <table>
+ <tr>
+ <td><label for="mail_">[% l('Email address') %]</label></td>
+ <td><input id="mail_" type="text" name="mail" value="[% c.request.params.mail %]" /></td>
+ </tr><tr>
+ <td colspan="2" style="text-align:center;">
+ <button type="submit" value="[% l('Reset password') %]">[% l('Reset password') %]</button>
+ </td>
+ </tr>
+ </table>
+</form>
+</div>
diff --git a/root/index.tt b/root/index.tt
index 8e1720c..de256f6 100644
--- a/root/index.tt
+++ b/root/index.tt
@@ -14,8 +14,7 @@
</div>
<div id="login_form_line">
<span><a href="/register">[% l('Register') %]</a> |
- @todo [% l('Forgotten password?') %]
- <!--<a href="/forgot_password">[% l('Forgotten password?') %]</a> -->
+ <a href="/forgot_password">[% l('Forgotten password?') %]</a>
</span>
</div>
</form>