diff options
author | Marius Halden <marius.h@lden.org> | 2020-09-29 14:23:52 +0200 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2020-09-29 14:23:52 +0200 |
commit | a27ce1524d801d2742a2bdb6ec1da45126d64353 (patch) | |
tree | 64123c4e17dc1776aa0a7cd65ee01d49d3e7d978 /perllib/FixMyStreet/App/Controller/Auth.pm | |
parent | 377bd96aab7cad3434185c30eb908c9da447fe40 (diff) | |
parent | 2773c60226b9370fe8ee00f7b205b571bb87c3b5 (diff) |
Merge tag 'v3.0.1' into fiksgatami-dev
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 164 |
1 files changed, 130 insertions, 34 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index c194045b9..cecfa318c 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -44,13 +44,12 @@ sub general : Path : Args(0) { # decide which action to take $c->detach('code_sign_in') if $clicked_sign_in_by_code || ($data_email && !$data_password); - if (!$data_username && !$data_password && !$data_email) { - $c->detach('social/facebook_sign_in') if $c->get_param('facebook_sign_in'); - $c->detach('social/twitter_sign_in') if $c->get_param('twitter_sign_in'); + if (!$data_username && !$data_password && !$data_email && $c->get_param('social_sign_in')) { + $c->forward('social/handle_sign_in'); } - $c->forward( 'sign_in', [ $data_username ] ) - && $c->detach( 'redirect_on_signin', [ $c->get_param('r') ] ); + $c->forward( 'sign_in', [ $data_username ] ) + && $c->detach( 'redirect_on_signin', [ $c->get_param('r') ] ); } @@ -68,6 +67,25 @@ sub forgot : Path('forgot') : Args(0) { $c->detach('code_sign_in'); } +sub expired : Path('expired') : Args(0) { + my ( $self, $c ) = @_; + + $c->detach('/page_error_403_access_denied', []) unless $c->user_exists; + + my $expiry = $c->cobrand->call_hook('password_expiry'); + $c->detach('/page_error_403_access_denied', []) unless $expiry; + + my $last_change = $c->user->get_extra_metadata('last_password_change') || 0; + my $midnight = int(time()/86400)*86400; + my $expired = $last_change + $expiry < $midnight; + $c->detach('/page_error_403_access_denied', []) unless $expired; + + $c->stash->{expired_password} = 1; + $c->stash->{template} = 'auth/create.html'; + return unless $c->req->method eq 'POST'; + $c->detach('code_sign_in', [ $c->user->email ]); +} + sub authenticate : Private { my ($self, $c, $type, $username, $password) = @_; return 1 if $type eq 'email' && $c->authenticate({ email => $username, email_verified => 1, password => $password }); @@ -122,9 +140,9 @@ they come back with a token (which contains the email/phone). =cut sub code_sign_in : Private { - my ( $self, $c ) = @_; + my ( $self, $c, $override_username ) = @_; - my $username = $c->stash->{username} = $c->get_param('username') || ''; + my $username = $c->stash->{username} = $override_username || $c->get_param('username') || ''; my $parsed = FixMyStreet::SMS->parse_username($username); @@ -180,10 +198,13 @@ sub email_sign_in : Private { name => $c->get_param('name'), password => $user->password, }; - $token_data->{facebook_id} = $c->session->{oauth}{facebook_id} - 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->get_param('oauth_need_email')) { + $token_data->{name} = $c->session->{oauth}{name} + if $c->session->{oauth}{name} && !$token_data->{name}; + $c->forward('set_oauth_token_data', [ $token_data ]); + } + if ($c->stash->{current_user}) { $token_data->{old_user_id} = $c->stash->{current_user}->id; $token_data->{r} = 'auth/change_email/success'; @@ -214,6 +235,14 @@ sub get_token : Private { return $data; } +sub set_oauth_token_data : Private { + my ( $self, $c, $token_data ) = @_; + + foreach (qw/facebook_id twitter_id oidc_id extra logout_redirect_uri change_password_uri/) { + $token_data->{$_} = $c->session->{oauth}{$_} if $c->session->{oauth}{$_}; + } +} + =head2 token Handle the 'email_sign_in' tokens. Find the account for the email address @@ -231,11 +260,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(); @@ -247,8 +276,15 @@ 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 + my $must_have_2fa = $c->cobrand->call_hook('must_have_2fa', $user) || ''; + if ($must_have_2fa ne 'skip') { + if ($user->has_2fa) { + $c->forward( 'token_2fa', [ $user, $url_token ] ); + } elsif ($c->cobrand->call_hook('must_have_2fa', $user)) { + $c->forward( 'signup_2fa', [ $user ] ); + } + } if ($data->{old_user_id}) { # Were logged in as old_user_id, want to switch to $user @@ -272,13 +308,74 @@ sub process_login : Private { $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->add_oidc_id( $data->{oidc_id} ) if $data->{oidc_id}; + $user->extra({ + %{ $user->get_extra() }, + %{ $data->{extra} } + }) if $data->{extra}; + $user->update_or_insert; $c->authenticate( { $type => $data->{$type}, $ver => 1 }, 'no_password' ); + foreach (qw/logout_redirect_uri change_password_uri/) { + if ($data->{$_}) { + $c->session->{oauth} ||= (); + $c->session->{oauth}{$_} = $data->{$_}; + } + } + + # send the user to their page $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 = FixMyStreet::Auth::GoogleAuth->new; + $c->stash->{qr_code} = $auth->qr_code($secret, $user->email, $c->cobrand->base_url); + $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. @@ -294,8 +391,11 @@ sub redirect_on_signin : Private { } unless ( $redirect ) { - $c->detach('redirect_to_categories') if $c->user->from_body && scalar @{ $c->user->categories }; - $redirect = 'my'; + my $inspector = $c->user->from_body && ( + scalar @{ $c->user->categories } || + scalar @{ $c->user->area_ids || [] } + ); + $redirect = $inspector ? 'my/inspector_redirect' : 'my'; } $redirect = 'my' if $redirect =~ /^admin/ && !$c->cobrand->admin_allow_user($c->user); if ( $c->cobrand->moniker eq 'zurich' ) { @@ -308,22 +408,6 @@ sub redirect_on_signin : Private { } } -=head2 redirect_to_categories - -Redirects the user to their body's reports page, prefiltered to whatever -categories this user has been assigned to. - -=cut - -sub redirect_to_categories : Private { - my ( $self, $c ) = @_; - - my $categories = $c->user->categories_string; - my $body_short = $c->cobrand->short_name( $c->user->from_body ); - - $c->res->redirect( $c->uri_for( "/reports/" . $body_short, { filter_category => $categories } ) ); -} - =head2 redirect Used when trying to view a page that requires sign in when you're not. @@ -429,6 +513,12 @@ Log the user out. Tell them we've done so. sub sign_out : Local { my ( $self, $c ) = @_; $c->logout(); + + if ( $c->sessionid && $c->session->{oauth} && $c->session->{oauth}{logout_redirect_uri} ) { + $c->response->redirect($c->session->{oauth}{logout_redirect_uri}); + delete $c->session->{oauth}{logout_redirect_uri}; + $c->detach; + } } sub ajax_sign_in : Path('ajax/sign_in') { @@ -436,7 +526,8 @@ sub ajax_sign_in : Path('ajax/sign_in') { my $return = {}; if ( $c->forward( 'sign_in', [ $c->get_param('email') ] ) ) { - $return->{name} = $c->user->name; + $return->{name} = $c->user->name || '-'; # App currently requires something returned + $return->{success} = 1; } else { $return->{error} = 1; } @@ -509,6 +600,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; |