aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller/Auth.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm')
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm113
1 files changed, 99 insertions, 14 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index c5a6cf9bf..b564a988c 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -6,8 +6,9 @@ BEGIN { extends 'Catalyst::Controller'; }
use Email::Valid;
use Net::Domain::TLD;
-use mySociety::AuthToken;
+use Digest::HMAC_SHA1 qw(hmac_sha1);
use JSON::MaybeXS;
+use MIME::Base64;
use Net::Facebook::Oauth2;
use Net::Twitter::Lite::WithAPIv1_1;
@@ -37,16 +38,17 @@ sub general : Path : Args(0) {
# all done unless we have a form posted to us
return unless $c->req->method eq 'POST';
- # decide which action to take
- $c->detach('facebook_sign_in') if $c->get_param('facebook_sign_in');
- $c->detach('twitter_sign_in') if $c->get_param('twitter_sign_in');
-
- my $clicked_password = $c->get_param('sign_in');
my $clicked_email = $c->get_param('email_sign_in');
+ my $data_address = $c->get_param('email');
my $data_password = $c->get_param('password_sign_in');
my $data_email = $c->get_param('name') || $c->get_param('password_register');
+ # decide which action to take
$c->detach('email_sign_in') if $clicked_email || ($data_email && !$data_password);
+ if (!$data_address && !$data_password && !$data_email) {
+ $c->detach('facebook_sign_in') if $c->get_param('facebook_sign_in');
+ $c->detach('twitter_sign_in') if $c->get_param('twitter_sign_in');
+ }
$c->forward( 'sign_in' )
&& $c->detach( 'redirect_on_signin', [ $c->get_param('r') ] );
@@ -137,6 +139,10 @@ sub email_sign_in : Private {
if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id};
$token_data->{twitter_id} = $c->session->{oauth}{twitter_id}
if $c->get_param('oauth_need_email') && $c->session->{oauth}{twitter_id};
+ if ($c->stash->{current_user}) {
+ $token_data->{old_email} = $c->stash->{current_user}->email;
+ $token_data->{r} = 'auth/change_email/success';
+ }
my $token_obj = $c->model('DB::Token')->create({
scope => 'email_sign_in',
@@ -144,7 +150,8 @@ sub email_sign_in : Private {
});
$c->stash->{token} = $token_obj->token;
- $c->send_email( 'login.txt', { to => $good_email } );
+ my $template = $c->stash->{email_template} || 'login.txt';
+ $c->send_email( $template, { to => $good_email } );
$c->stash->{template} = 'auth/token.html';
}
@@ -175,17 +182,40 @@ sub token : Path('/M') : Args(1) {
return;
}
- # Sign out in case we are another user
- $c->logout();
-
# find or create the user related to the token.
my $data = $token_obj->data;
- my $user = $c->model('DB::User')->find_or_create( { email => $data->{email} } );
+
+ if ($data->{old_email} && (!$c->user_exists || $c->user->email ne $data->{old_email})) {
+ $c->stash->{token_not_found} = 1;
+ return;
+ }
+
+ # sign out in case we are another user
+ $c->logout();
+
+ my $user = $c->model('DB::User')->find_or_new({ email => $data->{email} });
+
+ if ($data->{old_email}) {
+ # Were logged in as old_email, want to switch to email ($user)
+ if ($user->in_storage) {
+ my $old_user = $c->model('DB::User')->find({ email => $data->{old_email} });
+ if ($old_user) {
+ $old_user->adopt($user);
+ $user = $old_user;
+ $user->email($data->{email});
+ }
+ } else {
+ # Updating to a new (to the db) email address, easier!
+ $user = $c->model('DB::User')->find({ email => $data->{old_email} });
+ $user->email($data->{email});
+ }
+ }
+
$user->name( $data->{name} ) if $data->{name};
$user->password( $data->{password}, 1 ) if $data->{password};
$user->facebook_id( $data->{facebook_id} ) if $data->{facebook_id};
$user->twitter_id( $data->{twitter_id} ) if $data->{twitter_id};
- $user->update;
+ $user->update_or_insert;
$c->authenticate( { email => $user->email }, 'no_password' );
# send the user to their page
@@ -414,12 +444,13 @@ sub change_password : Local {
$c->detach( 'redirect' ) unless $c->user;
- # FIXME - CSRF check here
- # FIXME - minimum criteria for passwords (length, contain number, etc)
+ $c->forward('get_csrf_token');
# If not a post then no submission
return unless $c->req->method eq 'POST';
+ $c->forward('check_csrf_token');
+
# get the passwords
my $new = $c->get_param('new_password') // '';
my $confirm = $c->get_param('confirm') // '';
@@ -443,6 +474,60 @@ sub change_password : Local {
}
+=head2 change_email
+
+Let the user change their email.
+
+=cut
+
+sub change_email : Local {
+ my ( $self, $c ) = @_;
+
+ $c->detach( 'redirect' ) unless $c->user;
+
+ $c->forward('get_csrf_token');
+
+ # If not a post then no submission
+ return unless $c->req->method eq 'POST';
+
+ $c->forward('check_csrf_token');
+ $c->stash->{current_user} = $c->user;
+ $c->stash->{email_template} = 'change_email.txt';
+ $c->forward('email_sign_in');
+}
+
+sub get_csrf_token : Private {
+ my ( $self, $c ) = @_;
+
+ my $time = $c->stash->{csrf_time} || time();
+ my $hash = hmac_sha1("$time-" . ($c->sessionid || ""), $c->model('DB::Secret')->get);
+ $hash = encode_base64($hash, "");
+ $hash =~ s/=$//;
+ my $token = "$time-$hash";
+ $c->stash->{csrf_token} = $token unless $c->stash->{csrf_time};
+ return $token;
+}
+
+sub check_csrf_token : Private {
+ my ( $self, $c ) = @_;
+
+ my $token = $c->get_param('token') || "";
+ $token =~ s/ /+/g;
+ my ($time) = $token =~ /^(\d+)-[0-9a-zA-Z+\/]+$/;
+ $c->stash->{csrf_time} = $time;
+ $c->detach('no_csrf_token')
+ unless $time
+ && $time > time() - 3600
+ && $token eq $c->forward('get_csrf_token');
+ delete $c->stash->{csrf_time};
+}
+
+sub no_csrf_token : Private {
+ my ($self, $c) = @_;
+ $c->stash->{message} = _('Unknown error');
+ $c->stash->{template} = 'errors/generic.html';
+}
+
=head2 sign_out
Log the user out. Tell them we've done so.