diff options
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm new file mode 100644 index 000000000..b21981417 --- /dev/null +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -0,0 +1,178 @@ +package FixMyStreet::App::Controller::Auth; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Catalyst::Controller'; } + +use Email::Valid; +use Net::Domain::TLD; +use mySociety::AuthToken; +use Digest::SHA1 qw(sha1_hex); + +=head1 NAME + +FixMyStreet::App::Controller::Auth - Catalyst Controller + +=head1 DESCRIPTION + +Controller for all the authentication related pages - create account, login, +logout. + +=head1 METHODS + +=head2 index + +Present the user with a login / create account page. + +=cut + +sub general : Path : Args(0) { + my ( $self, $c ) = @_; + my $req = $c->req; + + # all done unless we have a form posted to us + return unless $req->method eq 'POST'; + + # check that the email is valid - otherwise flag an error + my $raw_email = $req->param('email') || ''; + my $email_checker = Email::Valid->new( + -mxcheck => 1, + -tldcheck => 1, + -fqdn => 1, + ); + + if ( my $good_email = $email_checker->address($raw_email) ) { + $c->stash->{email} = $good_email; + } + else { + $c->stash->{email} = $raw_email; + $c->stash->{email_error} = + $raw_email ? $email_checker->details : 'missing'; + return; + } + + # decide which action to take + $c->detach('create_account') if $req->param('create_account'); + + # hmm - should not get this far. 404 so that user knows there is a problem + # rather than it silently not working. + $c->detach('/page_not_found'); + +} + +=head2 create_account + +Create an account for the user, send them an email with confirm link and log +them in straight away. If the email address already has an account send them an +email with a password reset link (slightly leaks privacy information but +required to allow instant logins). + +=cut + +sub create_account : Private { + my ( $self, $c ) = @_; + my $email = $c->stash->{email}; + + # get account from the database + my $account = $c->model('DB::User')->find_or_new( { email => $email } ); + + # Deal with existing accounts by treating it like a password reset link + if ( $account->in_storage ) { + $c->stash->{tried_to_create_account} = 1; + $c->detach('email_reset'); + } + + # we have a new account + my $password = mySociety::AuthToken::random_token(); + $account->password( sha1_hex($password) ); + $account->insert; # save to database + + # log the user in, send them an email and redirect to the welcome page + $c->authenticate( { email => $email, password => $password } ); + $c->send_email( 'auth_new_account_welcome', { to => $email } ); + $c->res->redirect( $c->uri_for('welcome') ); +} + +=head2 welcome + +Page that new users are redirected to after they have created an account. + +=cut + +sub welcome : Local { + my ( $self, $c ) = @_; + + # FIXME - check that user is logged in! + # pass thru +} + +=head2 confirm + +Confirm that a user can receive email - url is .../confirm/$token + +We don't assume that the user is logged in, but if they are they are logged out +and then logged in as the user they are confirming. The token is destroyed at +the end of the request so it cannot be reused. + +=cut + +sub confirm : Local { + my ( $self, $c, $url_token ) = @_; + + # Use the token to confirm the user and return them. + my $user = $c->model('DB::User')->confirm_user_from_token($url_token); + + # If we did not get a user back then the token was not valid + return if !$user; + + # got a user back which is now confirmed - auth as them + $c->logout(); + $c->authenticate( { email => $user->email }, 'no_password' ); + $c->stash->{user_now_confirmed} = 1; + + # TODO - should we redirect somewhere - perhaps to pending problems? + return; +} + +=head2 logout + +Log the user out. Tell them we've done so. + +=cut + +sub logout : Local { + my ( $self, $c ) = @_; + $c->logout(); +} + +=head2 check_auth + +Utility page - returns a simple message 'OK' and a 200 response if the user is +authenticated and a 'Unauthorized' / 401 reponse if they are not. + +Mainly intended for testing but might also be useful for ajax calls. + +=cut + +sub check_auth : Local { + my ( $self, $c ) = @_; + + # choose the response + my ( $body, $code ) # + = $c->user + ? ( 'OK', 200 ) + : ( 'Unauthorized', 401 ); + + # set the response + $c->res->body($body); + $c->res->code($code); + + # NOTE - really a 401 response should also contain a 'WWW-Authenticate' + # header but we ignore that here. The spec is not keeping up with usage. + + return; +} + +__PACKAGE__->meta->make_immutable; + +1; |