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; |