diff options
Diffstat (limited to 'perllib/FixMyStreet/App/Controller')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 77 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Alert.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Around.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 113 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Moderate.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report.pm | 3 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/Update.pm | 1 |
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'); |