package Test2::Harness::UI::Controller::User;
use strict;
use warnings;
our $VERSION = '0.000127';
use Text::Xslate();
use Test2::Harness::UI::Util qw/share_dir/;
use Test2::Harness::UI::Response qw/resp error/;
use Email::Sender::Simple qw(sendmail);
use Email::Simple;
use Email::Simple::Creator;
use parent 'Test2::Harness::UI::Controller';
use Test2::Harness::UI::Util::HashBase qw/-title/;
sub handle {
my $self = shift;
my $req = $self->{+REQUEST};
my $res = resp(200);
$res->add_css('user.css');
$self->process_form($res) if keys %{$req->parameters};
my $user = $req->user;
unless($user) {
$res->raw_body($self->login());
return $res;
}
$self->{+TITLE} = 'User Settings';
my $tx = Text::Xslate->new(path => [share_dir('templates')]);
my $sort_val = {active => 1, disabled => 2, revoked => 3};
my $content = $tx->render(
'user.tx',
{
base_uri => $req->base->as_string,
user => $user,
keys => [sort { $sort_val->{$a->status} <=> $sort_val->{$b->status} } $user->api_keys->all],
emails => [sort { $b->is_primary <=> $a->is_primary || $a->domain cmp $b->domain || $a->local cmp $b->local } $user->emails],
perms => [sort { $a->project->name cmp $b->project->name } $user->permissions],
}
);
$res->raw_body($content);
return $res;
}
my %KEY_ACTION_MAP = (enable => 'active', disable => 'disabled', revoke => 'revoked');
sub process_form {
my $self = shift;
my ($res) = @_;
my $req = $self->{+REQUEST};
my $schema = $self->schema;
my $p = $req->parameters;
my $action = lc($p->{action} || '');
# This one we allow non-post, all others need post.
if ('logout' eq $action) {
$req->session_host->update({'user_id' => undef});
return $res->add_msg("You have been logged out.");
}
elsif ($action eq 'verify') {
my $evcode_id = $p->{verification_code}
or return $res->add_error("Invalid verification code");
my $code = $schema->resultset('EmailVerificationCode')->find({evcode_id => $evcode_id})
or return $res->add_error("Invalid verification code");
my $email = $code->email;
$email->update({verified => 1});
$code->delete();
return $res->add_msg("Email address verified");
}
die error(405) unless $req->method eq 'POST';
if ('login' eq $action) {
my $username = $p->{username} or return $res->add_error("username is required");
my $password = $p->{password} or return $res->add_error("password is required");
my $user = $schema->resultset('User')->find({username => $username});
return $res->add_error("Invalid username or password")
unless $user && $user->verify_password($password);
$req->session_host->update({'user_id' => $user->user_id});
return $res->add_msg("You have been logged in.");
}
if ('add email' eq $action) {
my $user = $req->user or return $res->add_error("You must be logged in");
my $addr = $p->{new_email} // '';
return $res->add_error("Invalid Email")
unless $addr =~ m/^(.+)@(.+\..+)$/;
my ($local, $domain) = ($1, $2);
if ($schema->resultset('Email')->find({local => $local, domain => $domain})) {
return $res->add_error("This email is already in use.");
}
my $email = eval { $schema->resultset('Email')->create({user_id => $user->user_id, local => $local, domain => $domain}) };
$res->add_error("Unable to add email: $@") unless $email;
$self->send_verification_code($email);
return $res->add_msg("Email '$local\@$domain' added, please check your email for the verification code");
}
if ('generate key' eq $action) {
my $key_name = $p->{key_name} or return $res->add_error("a key name is required");
my $user = $req->user or return $res->add_error("You must be logged in");
my $key = $user->gen_api_key($key_name);
return $res->add_msg("Key '$key_name' generated: " . $key->value);
}
if ('change password' eq $action) {
my $user = $req->user or return $res->add_error("You must be logged in");
my $old_password = $p->{old_password} or return $res->add_error("current password is required");
my $new_password_1 = $p->{new_password_1} or return $res->add_error("new password is required");
my $new_password_2 = $p->{new_password_2} or return $res->add_error("new password (again) is required");
return $res->add_error("New password fields do not match")
unless $new_password_1 eq $new_password_2;
return $res->add_error("Incorrect password") unless $user->verify_password($old_password);
$user->set_password($new_password_1);
return $res->add_msg("Password Changed.");
}
if ($p->{api_key_id} && $KEY_ACTION_MAP{$action}) {
my $key_id = $p->{api_key_id};
my $user = $req->user or return $res->add_error("You must be logged in");
my $key = $schema->resultset('ApiKey')->find({api_key_id => $key_id, user_id => $user->user_id});
return $res->add_error("Invalid key") unless $key;
$key->update({status => $KEY_ACTION_MAP{$action}});
return $res->add_msg("Key status changed.");
}
if ($p->{email_id}) {
my $user = $req->user or return $res->add_error("You must be logged in");
my $email = $schema->resultset('Email')->find({email_id => $p->{email_id}, user_id => $user->user_id});
return $res->add_error("Invalid Email") unless $email;
if ($action eq 'make primary') {
my $pri = $schema->resultset('PrimaryEmail')->update_or_create({user_id => $user->user_id, email_id => $p->{email_id}});
return $res->add_error("Could not make email primary: $@") unless $pri;
return $res->add_msg("Set primary email address.");
}
elsif ($action eq 'delete') {
$email->delete();
return $res->add_msg("Email address deleted");
}
elsif ($action eq 'send verification code') {
my $code = $self->send_verification_code($email);
return $res->add_msg("Verification code sent");
}
}
return $res->add_error("Invalid form submission");
}
sub login {
my $self = shift;
$self->{+TITLE} = 'Login';
my $tx = Text::Xslate->new(path => [share_dir('templates')]);
return $tx->render(
'login.tx',
{
base_uri => $self->{+REQUEST}->base->as_string,
}
);
}
sub send_verification_code {
my $self = shift;
my ($email) = @_;
my $schema = $self->schema;
my $code = $schema->resultset('EmailVerificationCode')->find_or_create({email_id => $email->email_id});
my $text = $code->evcode_id;
my $config = $self->{+CONFIG};
my $msg = Email::Simple->create(
header => [
To => $email->address,
From => $config->email,
Subject => "Email verification code",
],
body => "Verification code: $text\n",
);
sendmail($msg);
return $code;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Test2::Harness::UI::Controller::User
=head1 DESCRIPTION
=head1 SYNOPSIS
TODO
=head1 SOURCE
The source code repository for Test2-Harness-UI can be found at
F<http://github.com/Test-More/Test2-Harness-UI/>.
=head1 MAINTAINERS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=back
=head1 AUTHORS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=back
=head1 COPYRIGHT
Copyright 2019 Chad Granum E<lt>exodist7@gmail.comE<gt>.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
See F<http://dev.perl.org/licenses/>
=cut