diff options
Diffstat (limited to 'perllib/FixMyStreet')
-rw-r--r-- | perllib/FixMyStreet/App.pm | 18 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin/Bodies.pm | 8 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin/Users.pm | 29 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Alert.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 61 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth/Profile.pm | 35 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report.pm | 8 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/Update.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Default.pm | 16 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/FixMyStreet.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Oxfordshire.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 13 | ||||
-rw-r--r-- | perllib/FixMyStreet/Script/Reports.pm | 19 |
14 files changed, 110 insertions, 115 deletions
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index 27a5a4580..1173523bc 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -13,6 +13,7 @@ use FixMyStreet::Email::Sender; use FixMyStreet::PhotoStorage; use Utils; +use Auth::GoogleAuth; use Path::Tiny 'path'; use Try::Tiny; use Text::CSV; @@ -516,6 +517,23 @@ sub set_param { $c->req->params->{$param} = $value; } +=head2 check_2fa + +Given a user's secret, verifies a submitted code. + +=cut + +sub check_2fa { + my ($c, $secret32) = @_; + + if (my $code = $c->get_param('2fa_code')) { + my $auth = Auth::GoogleAuth->new; + return 1 if $auth->verify($code, 2, $secret32); + $c->stash->{incorrect_code} = 1; + } + return 0; +} + =head1 SEE ALSO L<FixMyStreet::App::Controller::Root>, L<Catalyst> diff --git a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm index 165fdc783..098c29ad4 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm @@ -269,14 +269,6 @@ sub update_contact : Private { } else { $contact->unset_extra_metadata( 'photo_required' ); } - if ( $c->get_param('inspection_required') ) { - $contact->set_extra_metadata( inspection_required => 1 ); - } else { - $contact->unset_extra_metadata( 'inspection_required' ); - } - if ( $c->get_param('reputation_threshold') ) { - $contact->set_extra_metadata( reputation_threshold => int($c->get_param('reputation_threshold')) ); - } if ( my @group = $c->get_param_list('group') ) { @group = grep { $_ } @group; if (scalar @group == 0) { diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm index fd18caf21..0d7c23fff 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm @@ -371,35 +371,6 @@ sub edit : Path : Args(1) { $user->area_ids( @area_ids ? \@area_ids : undef ); } - # Handle 'trusted' flag(s) - my @trusted_bodies = $c->get_param_list('trusted_bodies'); - if ( $c->user->is_superuser ) { - $user->user_body_permissions->search({ - body_id => { -not_in => \@trusted_bodies }, - permission_type => 'trusted', - })->delete; - foreach my $body_id (@trusted_bodies) { - $user->user_body_permissions->find_or_create({ - body_id => $body_id, - permission_type => 'trusted', - }); - } - } elsif ( $c->user->from_body ) { - my %trusted = map { $_ => 1 } @trusted_bodies; - my $body_id = $c->user->from_body->id; - if ( $trusted{$body_id} ) { - $user->user_body_permissions->find_or_create({ - body_id => $body_id, - permission_type => 'trusted', - }); - } else { - $user->user_body_permissions->search({ - body_id => $body_id, - permission_type => 'trusted', - })->delete; - } - } - # Update the categories this user operates in if ( $user->from_body ) { $c->stash->{body} = $user->from_body; diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm index 1060c080b..755602562 100644 --- a/perllib/FixMyStreet/App/Controller/Alert.pm +++ b/perllib/FixMyStreet/App/Controller/Alert.pm @@ -283,7 +283,7 @@ sub send_confirmation_email : Private { my $user = $c->stash->{alert}->user; - # Superusers using 2FA can not log in by code + # People using 2FA can not log in by code $c->detach( '/page_error_403_access_denied', [] ) if $user->has_2fa; my $token = $c->model("DB::Token")->create( diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index e23690368..041a8b76e 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -241,11 +241,11 @@ sub token : Path('/M') : Args(1) { && (!$c->user_exists || $c->user->id ne $data->{old_user_id}); my $type = $data->{login_type} || 'email'; - $c->detach( '/auth/process_login', [ $data, $type ] ); + $c->detach( '/auth/process_login', [ $data, $type, $url_token ] ); } sub process_login : Private { - my ( $self, $c, $data, $type ) = @_; + my ( $self, $c, $data, $type, $url_token ) = @_; # sign out in case we are another user $c->logout(); @@ -257,8 +257,9 @@ sub process_login : Private { $c->detach( '/page_error_403_access_denied', [] ) if FixMyStreet->config('SIGNUPS_DISABLED') && !$user->in_storage && !$data->{old_user_id}; - # Superusers using 2FA can not log in by code - $c->detach( '/page_error_403_access_denied', [] ) if $user->has_2fa; + # People using 2FA need to supply a code + $c->forward( 'token_2fa', [ $user, $url_token ] ) if $user->has_2fa; + $c->forward( 'signup_2fa', [ $user ] ) if $c->cobrand->call_hook('must_have_2fa', $user); if ($data->{old_user_id}) { # Were logged in as old_user_id, want to switch to $user @@ -303,6 +304,53 @@ sub process_login : Private { $c->detach( 'redirect_on_signin', [ $data->{r}, $data->{p} ] ); } +=head2 token_2fa + +Used after clicking an email token link to request a 2FA code + +=cut + +sub token_2fa : Private { + my ($self, $c, $user, $url_token) = @_; + + return if $c->check_2fa($user->has_2fa); + + $c->stash->{form_action} = $c->req->path; + $c->stash->{token} = $url_token; + $c->stash->{template} = 'auth/2fa/form.html'; + $c->detach; +} + +sub signup_2fa : Private { + my ($self, $c, $user) = @_; + + $c->stash->{form_action} = $c->req->path; + $c->stash->{template} = 'auth/2fa/intro.html'; + my $action = $c->get_param('2fa_action') || ''; + + my $secret; + if ($action eq 'confirm') { + $secret = $c->get_param('secret32'); + if ($c->check_2fa($secret)) { + $user->set_extra_metadata('2fa_secret' => $secret); + $user->update; + $c->stash->{stage} = 'success'; + return; + } else { + $action = 'activate'; # Incorrect code, reshow + } + } + + if ($action eq 'activate') { + my $auth = Auth::GoogleAuth->new; + $c->stash->{qr_code} = $auth->qr_code($secret, $user->email, 'FixMyStreet'); + $c->stash->{secret32} = $auth->secret32; + $c->stash->{stage} = 'activate'; + } + + $c->detach; +} + =head2 redirect_on_signin Used after signing in to take the person back to where they were. @@ -540,6 +588,11 @@ sub check_auth : Local { return; } +sub two_factor_setup_success : Private { + my ($self, $c) = @_; + # Only here to be detached to after setup success +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Auth/Profile.pm b/perllib/FixMyStreet/App/Controller/Auth/Profile.pm index 87aff2261..91ffac205 100644 --- a/perllib/FixMyStreet/App/Controller/Auth/Profile.pm +++ b/perllib/FixMyStreet/App/Controller/Auth/Profile.pm @@ -188,23 +188,38 @@ sub generate_token : Path('/auth/generate_token') { if ($c->get_param('generate_token')) { my $token = mySociety::AuthToken::random_token(); $c->user->set_extra_metadata('access_token', $token); + $c->user->update; $c->stash->{token_generated} = 1; } - if ($c->get_param('toggle_2fa') && $c->user->is_superuser) { - if ($has_2fa) { - $c->user->unset_extra_metadata('2fa_secret'); - $c->stash->{toggle_2fa_off} = 1; + my $action = $c->get_param('2fa_action') || ''; + $action = 'deactivate' if $c->get_param('2fa_deactivate'); + $action = 'activate' if $c->get_param('2fa_activate'); + $action = 'activate' if $action eq 'deactivate' && $has_2fa && $c->cobrand->call_hook('must_have_2fa', $c->user); + + my $secret; + if ($action eq 'deactivate') { + $c->user->unset_extra_metadata('2fa_secret'); + $c->user->update; + $c->stash->{toggle_2fa_off} = 1; + } elsif ($action eq 'confirm') { + $secret = $c->get_param('secret32'); + if ($c->check_2fa($secret)) { + $c->user->set_extra_metadata('2fa_secret', $secret); + $c->user->update; + $c->stash->{stage} = 'success'; + $has_2fa = 1; } else { - my $auth = Auth::GoogleAuth->new; - $c->stash->{qr_code} = $auth->qr_code(undef, $c->user->email, 'FixMyStreet'); - $c->stash->{secret32} = $auth->secret32; - $c->user->set_extra_metadata('2fa_secret', $auth->secret32); - $c->stash->{toggle_2fa_on} = 1; + $action = 'activate'; # Incorrect code, reshow } } - $c->user->update(); + if ($action eq 'activate') { + my $auth = Auth::GoogleAuth->new; + $c->stash->{qr_code} = $auth->qr_code($secret, $c->user->email, 'FixMyStreet'); + $c->stash->{secret32} = $auth->secret32; + $c->stash->{stage} = 'activate'; + } } $c->stash->{has_2fa} = $has_2fa ? 1 : 0; diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 692379de6..89350b1cb 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -356,8 +356,6 @@ sub delete :Chained('id') :Args(0) { $p->lastupdate( \'current_timestamp' ); $p->update; - $p->user->update_reputation(-1); - $c->model('DB::AdminLog')->create( { user => $c->user->obj, admin_user => $c->user->from_body->name, @@ -408,7 +406,6 @@ sub inspect : Private { my $valid = 1; my $update_text = ''; - my $reputation_change = 0; my %update_params = (); if ($permissions->{report_inspect}) { @@ -463,8 +460,6 @@ sub inspect : Private { $update_params{problem_state} = $problem->state; my $state = $problem->state; - $reputation_change = 1 if $c->cobrand->reputation_increment_states->{$state}; - $reputation_change = -1 if $c->cobrand->reputation_decrement_states->{$state}; # If an inspector has changed the state, subscribe them to # updates @@ -518,9 +513,6 @@ sub inspect : Private { $c->cobrand->call_hook(report_inspect_update_extra => $problem); if ($valid) { - if ( $reputation_change != 0 ) { - $problem->user->update_reputation($reputation_change); - } $problem->lastupdate( \'current_timestamp' ); $problem->update; if ($update_text || %update_params) { diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 907834b3a..612c76c0c 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -1613,7 +1613,7 @@ sub redirect_or_confirm_creation : Private { return 1; } - # Superusers using 2FA can not log in by code + # People using 2FA can not log in by code $c->detach( '/page_error_403_access_denied', [] ) if $report->user->has_2fa; # otherwise email or text a confirm token to them. diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 28a58d4f8..1dc337c48 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -501,7 +501,7 @@ sub redirect_or_confirm_creation : Private { return 1; } - # Superusers using 2FA can not log in by code + # People using 2FA can not log in by code $c->detach( '/page_error_403_access_denied', [] ) if $update->user->has_2fa; my $data = $c->stash->{token_data}; diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index d2929dcc0..620183078 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -748,12 +748,6 @@ sub available_permissions { contribute_as_body => _("Create reports/updates as the council"), default_to_body => _("Default to creating reports/updates as the council"), view_body_contribute_details => _("See user detail for reports created as the council"), - - # NB this permission is special in that it can be assigned to users - # without their from_body being set. It's included here for - # reference, but left commented out because it's not assigned in the - # same way as other permissions. - # trusted => _("Trusted to make reports that don't need to be inspected"), }, _("Users") => { user_edit => _("Edit users' details/search for their reports"), @@ -1227,16 +1221,6 @@ sub category_extra_hidden { return 0; } -=item reputation_increment_states/reputation_decrement_states - -Get a hashref of states that cause the reporting user's reputation to be -incremented/decremented, if a report is changed to this state upon inspection. - -=cut - -sub reputation_increment_states { {} }; -sub reputation_decrement_states { {} }; - sub traffic_management_options { return [ _("Yes"), diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm index e830b10b2..c69156c64 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm @@ -310,4 +310,10 @@ sub suppress_reporter_alerts { return 0; } +sub must_have_2fa { + my ($self, $user) = @_; + return 1 if $user->is_superuser; + return 0; +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index f99cdf84d..6f6284c7a 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -172,12 +172,6 @@ sub admin_pages { return $pages; } -sub reputation_increment_states { - return { - 'action scheduled' => 1, - }; -} - sub user_extra_fields { return [ 'initials' ]; } diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 85fdc790b..805ea4776 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -438,9 +438,6 @@ sub has_permission_to { my $cobrand = $self->result_source->schema->cobrand; my $cobrand_perms = $cobrand->available_permissions; my %available = map { %$_ } values %$cobrand_perms; - # The 'trusted' permission is never set in the cobrand's - # available_permissions (see note there in Default.pm) so include it here. - $available{trusted} = 1; return 0 unless $available{$permission_type}; return 1 if $self->is_superuser; @@ -498,7 +495,7 @@ sub admin_user_body_permissions { sub has_2fa { my $self = shift; - return $self->is_superuser && $self->get_extra_metadata('2fa_secret'); + return $self->get_extra_metadata('2fa_secret'); } sub contributing_as { @@ -600,14 +597,6 @@ sub is_planned_report { return scalar grep { $_->report_id == $id } @{$self->active_user_planned_reports}; } -sub update_reputation { - my ( $self, $change ) = @_; - - my $reputation = $self->get_extra_metadata('reputation') || 0; - $self->set_extra_metadata( reputation => $reputation + $change); - $self->update; -} - has categories => ( is => 'ro', lazy => 1, diff --git a/perllib/FixMyStreet/Script/Reports.pm b/perllib/FixMyStreet/Script/Reports.pm index 59de86db1..9725f1781 100644 --- a/perllib/FixMyStreet/Script/Reports.pm +++ b/perllib/FixMyStreet/Script/Reports.pm @@ -148,25 +148,6 @@ sub send(;$) { } $reporters{ $sender } ||= $sender->new(); - my $inspection_required = $sender_info->{contact} - ? $sender_info->{contact}->get_extra_metadata('inspection_required') - : undef; - if ( $inspection_required ) { - my $reputation_threshold = $sender_info->{contact}->get_extra_metadata('reputation_threshold') || 0; - my $reputation_threshold_met = 0; - if ( $reputation_threshold > 0 ) { - my $user_reputation = $row->user->get_extra_metadata('reputation') || 0; - $reputation_threshold_met = $user_reputation >= $reputation_threshold; - } - unless ( - $row->user->has_permission_to( trusted => $row->bodies_str_ids ) || - $reputation_threshold_met - ) { - $skip = 1; - debug_print("skipped because not yet inspected", $row->id) if $debug_mode; - } - } - if ( $reporters{ $sender }->should_skip( $row, $debug_mode ) ) { $skip = 1; debug_print("skipped by sender " . $sender_info->{method} . " (might be due to previous failed attempts?)", $row->id) if $debug_mode; |