| File: | lib/Yukki/Web/Controller/Login.pm |
| Coverage: | 37.1% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Yukki::Web::Controller::Login; | ||||||
| 2 | |||||||
| 3 | 1 1 | 617 3 | use v5.24; | ||||
| 4 | 1 1 1 | 3 1 5 | use utf8; | ||||
| 5 | 1 1 1 | 10 1 3 | use Moo; | ||||
| 6 | |||||||
| 7 | with 'Yukki::Web::Controller'; | ||||||
| 8 | |||||||
| 9 | 1 1 1 | 184 2 12 | use Email::Address; | ||||
| 10 | 1 1 1 | 3 1 3 | use Yukki::Error qw( http_throw ); | ||||
| 11 | 1 1 1 | 145 1 5 | use Yukki::TextUtil qw( dump_file ); | ||||
| 12 | |||||||
| 13 | 1 1 1 | 127 2 3 | use namespace::clean; | ||||
| 14 | |||||||
| 15 | # ABSTRACT: shows the login page and handles login | ||||||
| 16 | |||||||
| 17 - 27 | =head1 DESCRIPTION Shows the login page and handles login. =head1 METHODS =head2 fire Routes page reqquests to L</show_login_page>, submit requests to L</check_login_submission>, and exit requests to L</logout>. =cut | ||||||
| 28 | |||||||
| 29 | sub fire { | ||||||
| 30 | 1 | 1 | 2 | my ($self, $ctx) = @_; | |||
| 31 | |||||||
| 32 | 1 | 1 | my $res; | ||||
| 33 | 1 | 12 | my $action = $ctx->request->path_parameters->{action}; | ||||
| 34 | 1 0 | 19 0 | if ($action eq 'page') { $self->show_login_page($ctx) } | ||||
| 35 | 1 | 4 | elsif ($action eq 'submit') { $self->check_login_submission($ctx) } | ||||
| 36 | 0 | 0 | elsif ($action eq 'profile') { $self->show_profile_page($ctx) } | ||||
| 37 | 0 | 0 | elsif ($action eq 'update') { $self->update_profile($ctx) } | ||||
| 38 | 0 | 0 | elsif ($action eq 'exit') { $self->logout($ctx) } | ||||
| 39 | else { | ||||||
| 40 | 0 | 0 | http_throw('That login action does not exist.', { | ||||
| 41 | status => 'NotFound', | ||||||
| 42 | }) | ||||||
| 43 | } | ||||||
| 44 | } | ||||||
| 45 | |||||||
| 46 - 50 | =head2 show_login_page Calls L<Yukki::Web::View::Login/page> to display the login page. =cut | ||||||
| 51 | |||||||
| 52 | sub show_login_page { | ||||||
| 53 | 0 | 1 | 0 | my ($self, $ctx) = @_; | |||
| 54 | |||||||
| 55 | 0 | 0 | $ctx->response->body( $self->view('Login')->page($ctx) ); | ||||
| 56 | } | ||||||
| 57 | |||||||
| 58 - 62 | =head2 show_profile_page Calls L<Yukki::Web::View::Login/profile> to display the profile page. =cut | ||||||
| 63 | |||||||
| 64 | sub show_profile_page { | ||||||
| 65 | 0 | 1 | 0 | my ($self, $ctx, $name, $email) = @_; | |||
| 66 | |||||||
| 67 | 0 | 0 | $ctx->response->body( | ||||
| 68 | $self->view('Login')->profile($ctx, $name, $email) | ||||||
| 69 | ); | ||||||
| 70 | } | ||||||
| 71 | |||||||
| 72 - 76 | =head2 update_profile Validates the input user information and updates the user. Redirects the user back to the profile page. =cut | ||||||
| 77 | |||||||
| 78 | sub update_profile { | ||||||
| 79 | 0 | 1 | 0 | my ($self, $ctx) = @_; | |||
| 80 | |||||||
| 81 | http_throw('You are not authorized to run this action.', { | ||||||
| 82 | status => 'Forbidden', | ||||||
| 83 | 0 | 0 | }) unless $ctx->session->{user}; | ||||
| 84 | |||||||
| 85 | 0 | 0 | my $login_name = $ctx->request->body_parameters->{login_name}; | ||||
| 86 | |||||||
| 87 | 0 | 0 | unless ($login_name eq $ctx->session->{user}{login_name}) { | ||||
| 88 | 0 | 0 | $ctx->add_errors('Are you sure you are logged in as the correct user? Please make sure and try again.'); | ||||
| 89 | 0 | 0 | $ctx->response->redirect('/profile'); | ||||
| 90 | 0 | 0 | return; | ||||
| 91 | } | ||||||
| 92 | |||||||
| 93 | 0 | 0 | my $name = $ctx->request->body_parameters->{name}; | ||||
| 94 | 0 | 0 | my $email = $ctx->request->body_parameters->{email}; | ||||
| 95 | |||||||
| 96 | 0 0 | 0 0 | $name =~ s/^\s+//; $name =~ s/\s+$//; | ||||
| 97 | 0 0 | 0 0 | $email =~ s/^\s+//; $email =~ s/\s+$//; | ||||
| 98 | |||||||
| 99 | 0 | 0 | my $invalid = 0; | ||||
| 100 | 0 | 0 | unless ($name =~ /\S+/) { | ||||
| 101 | 0 | 0 | $ctx->add_errors('name must contain at least one letter'); | ||||
| 102 | 0 | 0 | $invalid++; | ||||
| 103 | } | ||||||
| 104 | |||||||
| 105 | 0 | 0 | my @emails = Email::Address->parse($email); | ||||
| 106 | 0 | 0 | if (@emails != 1) { | ||||
| 107 | 0 | 0 | $ctx->add_errors('that does not appear to be an email address'); | ||||
| 108 | 0 | 0 | $invalid++; | ||||
| 109 | } | ||||||
| 110 | |||||||
| 111 | 0 | 0 | if ($invalid) { | ||||
| 112 | 0 | 0 | return $self->show_profile_page($ctx, $name, $email); | ||||
| 113 | } | ||||||
| 114 | |||||||
| 115 | 1 1 1 | 909 28038 6 | use DDP; | ||||
| 116 | |||||||
| 117 | 0 | 0 | my %user = $ctx->session->{user}->%*; | ||||
| 118 | 0 | 0 | p %user; | ||||
| 119 | 0 | 0 | $user{name} = $name; | ||||
| 120 | 0 | 0 | $user{email} = $email; | ||||
| 121 | |||||||
| 122 | 0 | 0 | my $password_old = $ctx->request->body_parameters->{password_old}; | ||||
| 123 | 0 | 0 | my $password_new = $ctx->request->body_parameters->{password_new}; | ||||
| 124 | 0 | 0 | my $password_con = $ctx->request->body_parameters->{password_confirm}; | ||||
| 125 | |||||||
| 126 | # Only activate password check/reset if they use it | ||||||
| 127 | 0 | 0 | if (length $password_old) { | ||||
| 128 | my $okay = $self->check_password( | ||||||
| 129 | $ctx->session->{user}, | ||||||
| 130 | 0 | 0 | $password_old, | ||||
| 131 | ); | ||||||
| 132 | |||||||
| 133 | 0 | 0 | unless ($okay) { | ||||
| 134 | 0 | 0 | $ctx->add_errors('the current password you entered is incorrect'); | ||||
| 135 | 0 | 0 | return $self->show_profile_page($ctx, $name, $email); | ||||
| 136 | } | ||||||
| 137 | |||||||
| 138 | 0 | 0 | if (length($password_new) == 0) { | ||||
| 139 | 0 | 0 | $ctx->add_errors('you must enter a new password'); | ||||
| 140 | 0 | 0 | return $self->show_profile_page($ctx, $name, $email); | ||||
| 141 | } | ||||||
| 142 | |||||||
| 143 | 0 | 0 | if ($password_old eq $password_new) { | ||||
| 144 | 0 | 0 | $ctx->add_errors('the new and old passwords you entered are the same'); | ||||
| 145 | 0 | 0 | return $self->show_profile_page($ctx, $name, $email); | ||||
| 146 | } | ||||||
| 147 | |||||||
| 148 | 0 | 0 | if ($password_new ne $password_con) { | ||||
| 149 | 0 | 0 | $ctx->add_errors('the new passwords you entered do not match'); | ||||
| 150 | 0 | 0 | return $self->show_profile_page($ctx, $name, $email); | ||||
| 151 | } | ||||||
| 152 | |||||||
| 153 | 0 | 0 | my $digest = $self->app->hasher; | ||||
| 154 | 0 | 0 | $digest->add($password_new); | ||||
| 155 | 0 | 0 | $user{password} = $digest->generate; | ||||
| 156 | } | ||||||
| 157 | |||||||
| 158 | 0 | 0 | my $user_file = $self->app->locate('user_path', $login_name); | ||||
| 159 | 0 | 0 | chmod 0600, "$user_file"; | ||||
| 160 | 0 | 0 | dump_file($user_file, \%user); | ||||
| 161 | 0 | 0 | chmod 0400, "$user_file"; | ||||
| 162 | |||||||
| 163 | 0 | 0 | $ctx->session->{user} = \%user; | ||||
| 164 | |||||||
| 165 | 0 | 0 | $ctx->response->redirect('/profile'); | ||||
| 166 | 0 | 0 | return; | ||||
| 167 | } | ||||||
| 168 | |||||||
| 169 - 173 | =head2 check_password Checks that the user's password is valid. =cut | ||||||
| 174 | |||||||
| 175 | sub check_password { | ||||||
| 176 | 1 | 1 | 2 | my ($self, $user, $password) = @_; | |||
| 177 | |||||||
| 178 | return scalar $self->app->hasher->validate( | ||||||
| 179 | $user->{password}, | ||||||
| 180 | 1 | 9 | $password, | ||||
| 181 | ); | ||||||
| 182 | } | ||||||
| 183 | |||||||
| 184 - 188 | =head2 check_login_submission Authenticates a user login. =cut | ||||||
| 189 | |||||||
| 190 | sub check_login_submission { | ||||||
| 191 | 1 | 1 | 3 | my ($self, $ctx) = @_; | |||
| 192 | |||||||
| 193 | 1 | 11 | my $login_name = $ctx->request->body_parameters->{login_name}; | ||||
| 194 | 1 | 445 | my $password = $ctx->request->body_parameters->{password}; | ||||
| 195 | |||||||
| 196 | 1 | 47 | my $user = $self->model('User')->find(login_name => $login_name); | ||||
| 197 | |||||||
| 198 | 1 | 70 | if (not ($user and $self->check_password($user, $password))) { | ||||
| 199 | 0 | 0 | $ctx->add_errors('no such user or you typed your password incorrectly'); | ||||
| 200 | } | ||||||
| 201 | |||||||
| 202 | 1 | 317 | if ($ctx->has_errors) { | ||||
| 203 | 0 | 0 | $self->show_login_page($ctx); | ||||
| 204 | 0 | 0 | return; | ||||
| 205 | } | ||||||
| 206 | |||||||
| 207 | else { | ||||||
| 208 | 1 | 20 | $ctx->session->{user} = $user; | ||||
| 209 | |||||||
| 210 | 1 | 79 | $ctx->response->redirect($ctx->rebase_url('page/view/main')); | ||||
| 211 | 1 | 184 | return; | ||||
| 212 | } | ||||||
| 213 | } | ||||||
| 214 | |||||||
| 215 - 219 | =head2 logout Expires the session, causing logout. =cut | ||||||
| 220 | |||||||
| 221 | sub logout { | ||||||
| 222 | 0 | 1 | my ($self, $ctx) = @_; | ||||
| 223 | |||||||
| 224 | 0 | $ctx->session_options->{expire} = 1; | |||||
| 225 | 0 | $ctx->response->redirect($ctx->rebase_url('page/view/main')); | |||||
| 226 | } | ||||||
| 227 | |||||||
| 228 | 1; | ||||||