You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
18 KiB
491 lines
18 KiB
package MJB::Web::Controller::Auth;
|
|
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
|
use Try::Tiny;
|
|
use DateTime;
|
|
|
|
#=====
|
|
# This file handles the the user registration, login/logout,
|
|
# and resetting a forgotten password.
|
|
#
|
|
# It is a controller, the template files live in templates/auth.
|
|
#=====
|
|
|
|
#==
|
|
# GET /register | show_register | templates/auth/register.html.ep
|
|
#
|
|
# Send the user to whatever the default registration system is.
|
|
#==
|
|
sub register ( $c ) {
|
|
return $c->redirect_to( $c->url_for( 'show_dashboard' ) )
|
|
if exists $c->stash->{person} and $c->stash->{person}->id;
|
|
|
|
return $c->redirect_to( $c->url_for( 'show_register_stripe' ) )
|
|
if $c->config->{register}{default} eq 'stripe';
|
|
|
|
return $c->redirect_to( $c->url_for( 'show_register_invite' ) )
|
|
if $c->config->{register}{default} eq 'invite';
|
|
|
|
return $c->redirect_to( $c->url_for( 'show_register_open' ) )
|
|
if $c->config->{register}{default} eq 'open';
|
|
|
|
# No default registration system.
|
|
return $c->redirect_to( $c->url_for( 'show_homepage' ) );
|
|
}
|
|
|
|
#==
|
|
# GET /register/open | show_register_open | templates/auth/register_open.html.ep
|
|
#==
|
|
sub register_open ( $c ) {
|
|
# Don't allow this user registration method unless register.enable_open is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_open};
|
|
}
|
|
|
|
#==
|
|
# POST /register/open | do_register_open
|
|
# name | The name of the person who is registering an account
|
|
# email | The email address of the person registering the account
|
|
# password | The password they would like to use
|
|
# password_confirm | The same password again, in case they don't know it for sure
|
|
#
|
|
# Create an account for the user and login to that account once it has been created.
|
|
#==
|
|
sub do_register_open ( $c ) {
|
|
my $name = $c->stash->{form}->{name} = $c->param('name');
|
|
my $email = $c->stash->{form}->{email} = $c->param('email');
|
|
my $password = $c->stash->{form}->{password} = $c->param('password');
|
|
my $p_confirm = $c->stash->{form}->{password_confirm} = $c->param('password_confirm');
|
|
|
|
# Don't allow this user registration method unless register.enable_open is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_open};
|
|
|
|
push @{$c->stash->{errors}}, "Name is required" unless $name;
|
|
push @{$c->stash->{errors}}, "Email is required" unless $email;
|
|
push @{$c->stash->{errors}}, "Password is required" unless $password;
|
|
push @{$c->stash->{errors}}, "Confirm Password is required" unless $p_confirm;
|
|
|
|
return $c->redirect_error( 'show_register' )
|
|
if $c->stash->{errors};
|
|
|
|
push @{$c->stash->{errors}}, "Password and confirm password must match"
|
|
unless $p_confirm eq $password;
|
|
|
|
push @{$c->stash->{errors}}, "Password must be at least 8 characters"
|
|
unless length($password) >= 8;
|
|
|
|
push @{$c->stash->{errors}}, "That email address is already registered."
|
|
if $c->db->people( { email => $email } )->count;
|
|
|
|
return $c->redirect_error( 'show_register_open' )
|
|
if $c->stash->{errors};
|
|
|
|
my $person = try {
|
|
$c->db->storage->schema->txn_do( sub {
|
|
my $person = $c->db->resultset('Person')->create({
|
|
email => $c->param('email'),
|
|
name => $c->param('name'),
|
|
});
|
|
$person->new_related('auth_password', {})->set_password($c->param('password'));
|
|
|
|
# Notify the system about the new account.
|
|
$c->db->system_notes->create({
|
|
source => 'User Registration (Open)',
|
|
content => 'An account was created for ' . $person->email,
|
|
});
|
|
|
|
return $person;
|
|
});
|
|
} catch {
|
|
push @{$c->stash->{errors}}, "Account could not be created: $_";
|
|
};
|
|
|
|
return $c->redirect_error( 'show_register_open' )
|
|
if $c->stash->{errors};
|
|
|
|
# Log the user in and send them to the dashboard.
|
|
$c->session->{uid} = $person->id;
|
|
$c->redirect_to( $c->url_for( 'show_dashboard' ) );
|
|
}
|
|
|
|
#==
|
|
# GET /register/invite | show_register_invite | templates/auth/register_invite.html.ep
|
|
#==
|
|
sub register_invite ( $c ) {
|
|
$c->stash->{form}->{invite_code} ||= $c->param('code');
|
|
|
|
# Don't allow this user registration method unless register.enable_invite is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_invite};
|
|
}
|
|
|
|
#==
|
|
# POST /register/invite | do_register_invite
|
|
# name | The name of the person who is registering an account
|
|
# email | The email address of the person registering the account
|
|
# password | The password they would like to use
|
|
# password_confirm | The same password again, in case they don't know it for sure
|
|
# invite | A valid invite code
|
|
#
|
|
# Create an account for the user and login to that account once it has been created.
|
|
#
|
|
# If an invite code is used and is only valid once, it will be updated so it may no longer be used.
|
|
#==
|
|
sub do_register_invite ( $c ) {
|
|
my $name = $c->stash->{form}->{name} = $c->param('name');
|
|
my $email = $c->stash->{form}->{email} = $c->param('email');
|
|
my $password = $c->stash->{form}->{password} = $c->param('password');
|
|
my $p_confirm = $c->stash->{form}->{password_confirm} = $c->param('password_confirm');
|
|
my $invite = $c->stash->{form}->{invite_code} = $c->param('invite_code');
|
|
|
|
# Don't allow this user registration method unless register.enable_invite is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_invite};
|
|
|
|
push @{$c->stash->{errors}}, "Name is required" unless $name;
|
|
push @{$c->stash->{errors}}, "Email is required" unless $email;
|
|
push @{$c->stash->{errors}}, "Password is required" unless $password;
|
|
push @{$c->stash->{errors}}, "Confirm Password is required" unless $p_confirm;
|
|
push @{$c->stash->{errors}}, "Invite code is required" unless $invite;
|
|
|
|
return $c->redirect_error( 'show_register_invite' )
|
|
if $c->stash->{errors};
|
|
|
|
push @{$c->stash->{errors}}, "Password and confirm password must match"
|
|
unless $p_confirm eq $password;
|
|
|
|
push @{$c->stash->{errors}}, "Password must be at least 8 characters"
|
|
unless length($password) >= 8;
|
|
|
|
push @{$c->stash->{errors}}, "That email address is already registered."
|
|
if $c->db->people( { email => $email } )->count;
|
|
|
|
push @{$c->stash->{errors}}, "That invite code is not valid."
|
|
unless $c->db->invites( { code => $invite, is_active => 1 } )->count >= 1;
|
|
|
|
return $c->redirect_error( 'show_register_invite' )
|
|
if $c->stash->{errors};
|
|
|
|
my $person = try {
|
|
$c->db->storage->schema->txn_do( sub {
|
|
my $person = $c->db->resultset('Person')->create({
|
|
email => $c->param('email'),
|
|
name => $c->param('name'),
|
|
});
|
|
$person->new_related('auth_password', {})->set_password($c->param('password'));
|
|
|
|
# Notify the system about the new account.
|
|
$c->db->system_notes->create({
|
|
source => 'User Registration (Invite)',
|
|
content => 'An account was created for ' . $person->email,
|
|
});
|
|
|
|
# If a one-time use invite code was used, invalidate it.
|
|
my $invite_record = $c->db->invites( { code => $invite, is_active => 1 } )->first;
|
|
if ( $invite_record->is_one_time_use ) {
|
|
$invite_record->is_active( 0 );
|
|
$invite_record->update;
|
|
}
|
|
|
|
return $person;
|
|
});
|
|
} catch {
|
|
push @{$c->stash->{errors}}, "Account could not be created: $_";
|
|
};
|
|
|
|
return $c->redirect_error( 'show_register_invite' )
|
|
if $c->stash->{errors};
|
|
|
|
# Log the user in and send them to the dashboard.
|
|
$c->session->{uid} = $person->id;
|
|
$c->redirect_to( $c->url_for( 'show_dashboard' ) );
|
|
}
|
|
|
|
#==
|
|
# GET /register/stripe | show_register_stripe | templates/auth/register_stripe.html.ep
|
|
#==
|
|
sub register_stripe ( $c ) {
|
|
# Don't allow this user registration method unless register.enable_open is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_stripe};
|
|
}
|
|
|
|
#==
|
|
# POST /register/stripe | do_register_stripe
|
|
# name | The name of the person who is registering an account
|
|
# email | The email address of the person registering the account
|
|
# password | The password they would like to use
|
|
# password_confirm | The same password again, in case they don't know it for sure
|
|
#
|
|
# Create an account for the user and login to that account once it has been created.
|
|
#==
|
|
sub do_register_stripe ( $c ) {
|
|
my $name = $c->stash->{form}->{name} = $c->param('name');
|
|
my $email = $c->stash->{form}->{email} = $c->param('email');
|
|
my $password = $c->stash->{form}->{password} = $c->param('password');
|
|
my $p_confirm = $c->stash->{form}->{password_confirm} = $c->param('password_confirm');
|
|
|
|
# Don't allow this user registration method unless register.enable_open is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_stripe};
|
|
|
|
push @{$c->stash->{errors}}, "Name is required" unless $name;
|
|
push @{$c->stash->{errors}}, "Email is required" unless $email;
|
|
push @{$c->stash->{errors}}, "Password is required" unless $password;
|
|
push @{$c->stash->{errors}}, "Confirm Password is required" unless $p_confirm;
|
|
|
|
return $c->redirect_error( 'show_register_stripe' )
|
|
if $c->stash->{errors};
|
|
|
|
push @{$c->stash->{errors}}, "Password and confirm password must match"
|
|
unless $p_confirm eq $password;
|
|
|
|
push @{$c->stash->{errors}}, "Password must be at least 8 characters"
|
|
unless length($password) >= 8;
|
|
|
|
push @{$c->stash->{errors}}, "That email address is already registered."
|
|
if $c->db->people( { email => $email } )->count;
|
|
|
|
return $c->redirect_error( 'show_register_stripe' )
|
|
if $c->stash->{errors};
|
|
|
|
my $person = try {
|
|
$c->db->storage->schema->txn_do( sub {
|
|
my $person = $c->db->resultset('Person')->create({
|
|
email => $c->param('email'),
|
|
name => $c->param('name'),
|
|
});
|
|
$person->new_related('auth_password', {})->set_password($c->param('password'));
|
|
|
|
# Create the subscription.
|
|
$person->create_related('subscription', {});
|
|
|
|
# Notify the system about the new account.
|
|
$c->db->system_notes->create({
|
|
source => 'User Registration (Stripe)',
|
|
content => 'An account was created for ' . $person->email,
|
|
});
|
|
|
|
return $person;
|
|
});
|
|
} catch {
|
|
push @{$c->stash->{errors}}, "Account could not be created: $_";
|
|
};
|
|
|
|
return $c->redirect_error( 'show_register_stripe' )
|
|
if $c->stash->{errors};
|
|
|
|
# Log the user in asnd then send themn to the payment page.
|
|
$c->session->{uid} = $person->id;
|
|
$c->redirect_to(
|
|
$c->ua->get( $c->config->{stripe}->{backend} . '/stripe/get-checkout-link?lookup_key=' . $c->config->{stripe}->{lookup_key} )->result->json->{url}
|
|
);
|
|
}
|
|
|
|
#==
|
|
# GET /register/stripe/pay | show_register_stripe_pay | N/A
|
|
#
|
|
# Once a user has registered in the strip work flow, they will need to pay
|
|
# for their account before it is enabled. This route will bring them to the
|
|
# stripe subscription payment page -- in case it failed the first time.
|
|
#==
|
|
sub register_stripe_pay ( $c ) {
|
|
# Don't allow this user registration method unless register.enable_open is true.
|
|
return $c->redirect_to( $c->url_for( 'show_register' ) )
|
|
unless $c->config->{register}{enable_stripe};
|
|
|
|
$c->redirect_to(
|
|
$c->ua->get( $c->config->{stripe}->{backend} . '/stripe/get-checkout-link?lookup_key=' . $c->config->{stripe}->{lookup_key} )->result->json->{url}
|
|
);
|
|
}
|
|
|
|
#==
|
|
# GET /login | show_login | templates/auth/login.html.ep
|
|
#
|
|
# If a user is already logged in, redirect them to the dashboard instead
|
|
# of showing the login page.
|
|
#==
|
|
sub login ( $c ) {
|
|
if ( $c->stash->{person} ) {
|
|
$c->redirect_to( $c->url_for( 'show_dashboard' ) );
|
|
}
|
|
}
|
|
|
|
#==
|
|
# POST /login | do_login
|
|
# email - The email address of the account to login to.
|
|
# password - The password for the account to login to.
|
|
#
|
|
# Try to login to the account owned by the email address with the
|
|
# supplied password.
|
|
#
|
|
# If the account exists and password matches, set the session uid
|
|
# to the user's account id. This will load the correct account to
|
|
# $c->stash->{person} on the next page load.
|
|
#
|
|
# Show the login page with error messages when there has been an error.
|
|
#
|
|
# Redirect the user to the dashboard on successful login.
|
|
#==
|
|
sub do_login ( $c ) {
|
|
my $email = $c->stash->{form}->{email} = $c->param('email');
|
|
my $password = $c->stash->{form}->{password} = $c->param('password');
|
|
|
|
# Did we get an email address and a password?
|
|
push @{$c->stash->{errors}}, "You must supply an email address to login."
|
|
unless $email;
|
|
|
|
push @{$c->stash->{errors}}, "You must suply a password to login."
|
|
unless $password;
|
|
|
|
return $c->redirect_error( 'show_login' )
|
|
if $c->stash->{errors};
|
|
|
|
# Can we load a user account?
|
|
my $person = $c->db->resultset('Person')->find( { email => $email } )
|
|
or push @{$c->stash->{errors}}, "Invalid email address or password.";
|
|
|
|
return $c->redirect_error( 'show_login' )
|
|
if $c->stash->{errors};
|
|
|
|
# Does the user account we loaded have a password that matches the one supplied?
|
|
$person->auth_password->check_password( $password )
|
|
or push @{$c->stash->{errors}}, "Invalid email address or password.";
|
|
|
|
return $c->redirect_error( 'show_login' )
|
|
if $c->stash->{errors};
|
|
|
|
# Everything is good, log the user in and send them to the dashboard.
|
|
$c->session->{uid} = $person->id;
|
|
$c->redirect_to( $c->url_for( 'show_dashboard' ) );
|
|
}
|
|
|
|
#==
|
|
# POST /logout | do_logout
|
|
#
|
|
# Log a user out of their account.
|
|
#
|
|
# If an admin has logged into a user's account through the admin_become interface,
|
|
# then logging out will return the admin to their account instead of logging them
|
|
# out completely.
|
|
#==
|
|
sub do_logout ( $c ) {
|
|
|
|
# When an admin has impersonated a user, they'll have their uid
|
|
# stored to oid. When they logout, they are logging out of the
|
|
# impersonated user's account, back into their own account.
|
|
# If a url is set in the session, the admin is returned to that page.
|
|
if ( $c->session->{oid} ) {
|
|
$c->session->{uid} = delete $c->session->{oid};
|
|
if ( $c->session->{url} ) {
|
|
$c->redirect_to( $c->url_for( delete $c->session->{url} ) );
|
|
} else {
|
|
$c->redirect_to( $c->url_for( 'show_admin' ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Delete the session cookie and return them to the homepage.
|
|
undef $c->session->{uid};
|
|
$c->redirect_to( $c->url_for( 'show_homepage' ) );
|
|
}
|
|
|
|
#==
|
|
# GET /forgot | show_forgot | templates/auth/forgot.html.ep
|
|
#==
|
|
sub forgot ( $c ) { }
|
|
|
|
#==
|
|
# POST /forgot | do_forgot
|
|
# email | The email address to reset the password for
|
|
#
|
|
# When a user requests their password be reset, a token is created
|
|
# that can be used to reset the password.
|
|
#
|
|
# This token is sent to the user via email as a link they can click
|
|
# to go to the reset page.
|
|
#==
|
|
sub do_forgot ( $c ) {
|
|
my $email = $c->stash->{form}->{email} = $c->param('email');
|
|
|
|
my $person = $c->db->resultset('Person')->find( { email => $email } )
|
|
or push @{$c->stash->{errors}}, "There is no account with that email address.";
|
|
|
|
return $c->redirect_error( 'show_forgot' )
|
|
if $c->stash->{errors};
|
|
|
|
# Make a token for password resetting.
|
|
my $token = $c->stash->{token} = $person->create_auth_token( 'forgot' );
|
|
|
|
# Send the user an email with the link for resetting.
|
|
$c->send_email( 'forgot_password', {
|
|
send_to => $email,
|
|
link => 'https://' . $c->config->{domain_for_links} . "/reset/$token"
|
|
});
|
|
|
|
# Let the user know the next steps.
|
|
$c->flash( confirmation => 'Please check your email for a password reset link.' );
|
|
$c->redirect_to( $c->url_for( 'show_forgot' ) );
|
|
}
|
|
|
|
sub reset ( $c ) { }
|
|
|
|
#==
|
|
# POST /reset/:token
|
|
# password | The new password for the user
|
|
# password_confirm | The new password for the user, again
|
|
#
|
|
# This route is used to reset a password when somebody has a token for
|
|
# a password reset on an account.
|
|
#==
|
|
sub do_reset ( $c ) {
|
|
my $token = $c->param('token');
|
|
my $password = $c->stash->{form_password} = $c->param('password');
|
|
my $confirm = $c->stash->{form_password_confirm} = $c->param('password_confirm');
|
|
|
|
push @{$c->stash->{errors}}, "Password is required" unless $password;
|
|
push @{$c->stash->{errors}}, "Confirm Password is required" unless $confirm;
|
|
|
|
return $c->redirect_error( 'show_reset', { token => $token } )
|
|
if $c->stash->{errors};
|
|
|
|
push @{$c->stash->{errors}}, "Password and confirm password must match"
|
|
unless $confirm eq $password;
|
|
|
|
push @{$c->stash->{errors}}, "Password must be at least 8 characters"
|
|
unless length($password) >= 8;
|
|
|
|
return $c->redirect_error( 'show_reset', { token => $token } )
|
|
if $c->stash->{errors};
|
|
|
|
my $lower_time = DateTime->now;
|
|
$lower_time->subtract( minutes => 60 );
|
|
|
|
my $dtf = $c->db->storage->datetime_parser;
|
|
|
|
my $record = $c->db->auth_tokens->search( {
|
|
token => $token,
|
|
scope => 'forgot',
|
|
'me.created_at' => { '>=', $dtf->format_datetime($lower_time) },
|
|
}, { prefetch => 'person' })->first;
|
|
|
|
push @{$c->stash->{errors}}, "This token is not valid."
|
|
unless $record;
|
|
|
|
return $c->redirect_error( 'show_reset', { token => $token } )
|
|
if $c->stash->{errors};
|
|
|
|
# Change the user's password.
|
|
$record->person->auth_password->update_password( $password );
|
|
|
|
# Log the user into the account
|
|
$c->session->{uid} = $record->person->id;
|
|
|
|
# Delete this token.
|
|
$record->delete;
|
|
|
|
# Send them to the dashboard.
|
|
$c->redirect_to( $c->url_for( 'show_dashboard' ) );
|
|
}
|
|
|
|
1;
|
|
|