aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App/Controller')
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm77
-rw-r--r--perllib/FixMyStreet/App/Controller/Alert.pm6
-rw-r--r--perllib/FixMyStreet/App/Controller/Around.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm113
-rw-r--r--perllib/FixMyStreet/App/Controller/Moderate.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm1
8 files changed, 134 insertions, 72 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 4e288556f..e68eb00a5 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -215,7 +215,7 @@ sub bodies : Path('bodies') : Args(0) {
return;
}
- $c->forward( 'get_token' );
+ $c->forward( '/auth/get_csrf_token' );
my $edit_activity = $c->model('DB::ContactsHistory')->search(
undef,
@@ -232,7 +232,7 @@ sub bodies : Path('bodies') : Args(0) {
my $posted = $c->get_param('posted') || '';
if ( $posted eq 'body' ) {
$c->forward('check_for_super_user');
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my $params = $c->forward('body_params');
my $body = $c->model('DB::Body')->create( $params );
@@ -289,7 +289,7 @@ sub body : Path('body') : Args(1) {
$c->stash->{body_id} = $body_id;
$c->forward( 'check_for_super_user' );
- $c->forward( 'get_token' );
+ $c->forward( '/auth/get_csrf_token' );
$c->forward( 'lookup_body' );
$c->forward( 'fetch_all_bodies' );
$c->forward( 'body_form_dropdowns' );
@@ -318,7 +318,7 @@ sub update_contacts : Private {
my $editor = $c->forward('get_user');
if ( $posted eq 'new' ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my %errors;
@@ -370,7 +370,7 @@ sub update_contacts : Private {
}
} elsif ( $posted eq 'update' ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my @categories = $c->get_param_list('confirmed');
@@ -393,7 +393,7 @@ sub update_contacts : Private {
$c->stash->{updated} = _('Values updated');
} elsif ( $posted eq 'body' ) {
$c->forward('check_for_super_user');
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my $params = $c->forward( 'body_params' );
$c->stash->{body}->update( $params );
@@ -476,7 +476,7 @@ sub category_edit : Path('body') : Args(2) {
$c->stash->{body_id} = $body_id;
- $c->forward( 'get_token' );
+ $c->forward( '/auth/get_csrf_token' );
$c->forward( 'lookup_body' );
my $contact = $c->stash->{body}->contacts->search( { category => $category } )->first;
@@ -643,7 +643,7 @@ sub report_edit : Path('report_edit') : Args(1) {
$c->stash->{problem} = $problem;
- $c->forward('get_token');
+ $c->forward('/auth/get_csrf_token');
if ( $c->cobrand->moniker eq 'zurich' ) {
$c->stash->{page} = 'admin';
@@ -689,7 +689,7 @@ sub report_edit : Path('report_edit') : Args(1) {
->all ];
if ( $c->get_param('resend') ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
$problem->whensent(undef);
$problem->update();
@@ -699,7 +699,7 @@ sub report_edit : Path('report_edit') : Args(1) {
$c->forward( 'log_edit', [ $id, 'problem', 'resend' ] );
}
elsif ( $c->get_param('mark_sent') ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
$problem->whensent(\'current_timestamp');
$problem->update();
$c->stash->{status_message} = '<p><em>' . _('That problem has been marked as sent.') . '</em></p>';
@@ -717,7 +717,7 @@ sub report_edit : Path('report_edit') : Args(1) {
$c->forward('ban_user');
}
elsif ( $c->get_param('submit') ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my $done = 0;
my $edited = 0;
@@ -917,7 +917,7 @@ sub users: Path('users') : Args(0) {
}
} else {
- $c->forward('get_token');
+ $c->forward('/auth/get_csrf_token');
$c->forward('fetch_all_bodies');
# Admin users by default
@@ -942,7 +942,7 @@ sub update_edit : Path('update_edit') : Args(1) {
$c->detach( '/page_error_404_not_found' )
unless $update;
- $c->forward('get_token');
+ $c->forward('/auth/get_csrf_token');
$c->stash->{update} = $update;
@@ -965,7 +965,7 @@ sub update_edit : Path('update_edit') : Args(1) {
$c->stash->{update}->discard_changes;
}
elsif ( $c->get_param('submit') ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my $old_state = $update->state;
my $new_state = $c->get_param('state');
@@ -1047,12 +1047,12 @@ sub user_add : Path('user_edit') : Args(0) {
my ( $self, $c ) = @_;
$c->stash->{template} = 'admin/user_edit.html';
- $c->forward('get_token');
+ $c->forward('/auth/get_csrf_token');
$c->forward('fetch_all_bodies');
return unless $c->get_param('submit');
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
unless ($c->get_param('email')) {
$c->stash->{field_errors}->{email} = _('Please enter a valid email');
@@ -1084,7 +1084,7 @@ sub user_add : Path('user_edit') : Args(0) {
sub user_edit : Path('user_edit') : Args(1) {
my ( $self, $c, $id ) = @_;
- $c->forward('get_token');
+ $c->forward('/auth/get_csrf_token');
my $user = $c->model('DB::User')->find( { id => $id } );
$c->stash->{user} = $user;
@@ -1092,7 +1092,7 @@ sub user_edit : Path('user_edit') : Args(1) {
$c->forward('fetch_all_bodies');
if ( $c->get_param('submit') ) {
- $c->forward('check_token');
+ $c->forward('/auth/check_csrf_token');
my $edited = 0;
@@ -1120,12 +1120,7 @@ sub user_edit : Path('user_edit') : Args(1) {
my $existing_user = $c->model('DB::User')->search({ email => $user->email, id => { '!=', $user->id } })->first;
if ($existing_user) {
- foreach (qw(Problem Comment Alert)) {
- $c->model("DB::$_")
- ->search({ user_id => $user->id })
- ->update({ user_id => $existing_user->id });
- }
- $user->delete;
+ $existing_user->adopt($user);
$c->forward( 'log_edit', [ $id, 'user', 'merge' ] );
$c->res->redirect( $c->uri_for( 'user_edit', $existing_user->id ) );
} else {
@@ -1328,40 +1323,6 @@ sub get_user : Private {
return $user;
}
-=item get_token
-
-Generate a token based on user and secret
-
-=cut
-
-sub get_token : Private {
- my ( $self, $c ) = @_;
-
- my $secret = $c->model('DB::Secret')->get;
- my $user = $c->forward('get_user');
- my $token = sha1_hex($user . $secret);
- $c->stash->{token} = $token;
-
- return 1;
-}
-
-=item check_token
-
-Check that a token has been set on a request and it's the correct token. If
-not then display 404 page
-
-=cut
-
-sub check_token : Private {
- my ( $self, $c ) = @_;
-
- if ( !$c->get_param('token') || $c->get_param('token') ne $c->stash->{token} ) {
- $c->detach( '/page_error_404_not_found' );
- }
-
- return 1;
-}
-
=item log_edit
$c->forward( 'log_edit', [ $object_id, $object_type, $action_performed ] );
diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm
index ddda02abd..b578fbbcc 100644
--- a/perllib/FixMyStreet/App/Controller/Alert.pm
+++ b/perllib/FixMyStreet/App/Controller/Alert.pm
@@ -36,6 +36,8 @@ sub index : Path('') : Args(0) {
sub list : Path('list') : Args(0) {
my ( $self, $c ) = @_;
+ $c->forward('/auth/get_csrf_token');
+
return
unless $c->forward('setup_request')
&& $c->forward('prettify_pc')
@@ -112,6 +114,8 @@ Sign up to email alerts
sub subscribe_email : Private {
my ( $self, $c ) = @_;
+ $c->forward('/auth/check_csrf_token');
+
$c->stash->{errors} = [];
$c->forward('process_user');
@@ -146,6 +150,8 @@ sub subscribe_email : Private {
sub updates : Path('updates') : Args(0) {
my ( $self, $c ) = @_;
+ $c->forward('/auth/get_csrf_token');
+
$c->stash->{email} = $c->get_param('rznvy');
$c->stash->{problem_id} = $c->get_param('id');
}
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm
index b0340204a..b8f038ce3 100644
--- a/perllib/FixMyStreet/App/Controller/Around.pm
+++ b/perllib/FixMyStreet/App/Controller/Around.pm
@@ -156,6 +156,8 @@ sub display_location : Private {
# set the template to use
$c->stash->{template} = 'around/display_location.html';
+ $c->forward('/auth/get_csrf_token');
+
# get the lat,lng
my $latitude = $c->stash->{latitude};
my $longitude = $c->stash->{longitude};
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.
diff --git a/perllib/FixMyStreet/App/Controller/Moderate.pm b/perllib/FixMyStreet/App/Controller/Moderate.pm
index 77a3346dc..2d23417b9 100644
--- a/perllib/FixMyStreet/App/Controller/Moderate.pm
+++ b/perllib/FixMyStreet/App/Controller/Moderate.pm
@@ -57,6 +57,8 @@ sub report : Chained('moderate') : PathPart('report') : CaptureArgs(1) {
$c->detach unless $c->user_exists;
$c->detach unless $c->user->has_permission_to(moderate => $problem->bodies_str);
+ $c->forward('/auth/check_csrf_token');
+
my $original = $problem->find_or_new_related( moderation_original_data => {
title => $problem->title,
detail => $problem->detail,
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index b3e546c2c..89df4a52d 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -72,6 +72,7 @@ sub ajax : Path('ajax') : Args(1) {
sub _display : Private {
my ( $self, $c, $id ) = @_;
+ $c->forward('/auth/get_csrf_token');
$c->forward( 'load_problem_or_display_error', [ $id ] );
$c->forward( 'load_updates' );
$c->forward( 'format_problem_for_display' );
@@ -249,6 +250,8 @@ users too about this change, at which point we can delete:
sub delete :Local :Args(1) {
my ( $self, $c, $id ) = @_;
+ $c->forward('/auth/check_csrf_token');
+
$c->forward( 'load_problem_or_display_error', [ $id ] );
my $p = $c->stash->{problem};
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index af9ca50b5..9779a5e2a 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -81,6 +81,7 @@ sub report_new : Path : Args(0) {
# create the report - loading a partial if available
$c->forward('initialize_report');
+ $c->forward('/auth/get_csrf_token');
# work out the location for this report and do some checks
# Also show map if we're just updating the filters
@@ -96,6 +97,7 @@ sub report_new : Path : Args(0) {
# deal with the user and report and check both are happy
return unless $c->forward('check_form_submitted');
+ $c->forward('/auth/check_csrf_token');
$c->forward('process_user');
$c->forward('process_report');
$c->forward('/photo/process_photo');
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index 275a300bd..d03797f56 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -25,6 +25,7 @@ sub report_update : Path : Args(0) {
$c->forward('check_form_submitted')
or $c->go( '/report/display', [ $c->stash->{problem}->id ] );
+ $c->forward('/auth/check_csrf_token');
$c->forward('process_update');
$c->forward('process_user');
$c->forward('/photo/process_photo');