File Coverage

blib/lib/MojoMojo/Controller/User.pm
Criterion Covered Total %
statement 123 225 54.6
branch 19 68 27.9
condition 9 46 19.5
subroutine 26 34 76.4
pod 14 14 100.0
total 191 387 49.3


line stmt bran cond sub pod time code
1             package MojoMojo::Controller::User;
2              
3 35     35   16963 use strict;
  35         97  
  35         1247  
4 35     35   202 use parent qw/Catalyst::Controller::HTML::FormFu/;
  35         84  
  35         244  
5              
6 35     35   17353594 use Digest::MD5 qw/md5_hex/;
  35         99  
  35         1760  
7 35     35   22952 use Text::Password::Pronounceable;
  35         55639  
  35         483  
8              
9             my $auth_class = MojoMojo->config->{auth_class};
10              
11             =head1 NAME
12              
13             MojoMojo::Controller::User - Login/User Management Controller
14              
15              
16             =head1 DESCRIPTION
17              
18             This controller performs user management: registration, login, logout,
19             password recovery and profile editing.
20              
21              
22             =head1 ACTIONS
23              
24             =head2 login (/.login)
25              
26             Log in through the authentication system.
27              
28             =cut
29              
30             sub login : Global : FormConfig {
31 14     14   6144107 my ( $self, $c ) = @_;
32 14   100     78 $c->stash->{message} ||= $c->flash->{message};
33 14         39154 my $form = $c->stash->{form};
34              
35 14 100       913 if ( $form->submitted_and_valid ) {
36 10 100       9853 if ($c->req->method ne 'POST') {
    100          
37             # general error - we want a POST
38 1         59 $c->res->status(400);
39             } elsif (
40             $c->authenticate(
41             {
42             login => $form->param_value('login'),
43             pass => $form->param_value('pass'),
44             }
45             )
46             )
47             {
48              
49 8         88999 $c->stash->{user} = $c->user->obj;
50             $c->res->redirect( $c->uri_for( $c->stash->{path} ) )
51 8 100       2954 unless $c->stash->{template};
52 8         1615 return;
53             }
54             else {
55 1         7447 $c->stash->{fail} = 1;
56             $c->stash->{message} =
57 1         108 $c->loc('Could not authenticate that login.');
58             # big debate in #catalyst on the status code that should be returned if authentication failed
59             # 400 is too vague, 401 says "MUST supply WWW-Authenticate", 451 is a M$ extension
60             # also note that IE doesn't display the error page if it's shorter than 512 bytes (not the case here)
61 1         494 $c->res->status(403); # works fine with IE6
62             }
63             }
64 6   100     419 $c->stash->{template} ||= "user/login.tt";
65 35     35   6913 }
  35         80  
  35         339  
66              
67             =head2 logout ( /.logout )
68              
69             Log out the user.
70              
71             =cut
72              
73             sub logout : Global {
74 2     2 1 2381 my ( $self, $c ) = @_;
75 2         27 $c->logout;
76 2         3791 undef $c->stash->{user};
77              
78 2         215 $c->response->redirect( $c->uri_for('view') );
79 35     35   434718 }
  35         101  
  35         176  
80              
81             =head2 users ( /.users )
82              
83             Show a list of the active users with links to the pages they edited.
84              
85             =cut
86              
87             sub users : Global {
88 2     2 1 2415 my ( $elf, $c ) = @_;
89 2   50     13 my $res = $c->model("DBIC::Person")->search(
90             { active => 1 },
91             {
92             page => $c->req->param('page') || 1,
93             rows => 20,
94             order_by => 'login'
95             }
96             );
97 2         2398 $c->stash->{users} = $res;
98 2         206 $c->stash->{pager} = $res->pager;
99 2         2797 $c->stash->{template} = 'user/list.tt';
100 35     35   34663 }
  35         99  
  35         168  
101              
102             =head2 page_user
103              
104             Show a user's preferences
105              
106             =cut
107              
108             sub page_user : Private {
109 0     0 1 0 my ( $self, $c ) = @_;
110 0         0 my $user = $c->stash->{user};
111             my $login = (
112             $c->stash->{proto_pages}[-1]
113             ? $c->stash->{proto_pages}[-1]->{name} # FIXME: why not ->{name_orig}, like in editprofile() ?
114             : $c->stash->{page}->name
115 0 0       0 );
116 0         0 my $page_user = $c->model("DBIC::Person")->get_user($login);
117              
118 0 0 0     0 if (
      0        
      0        
119             $page_user
120             && $user
121             && ( $page_user->id eq $user->id || $user->is_admin() )
122             )
123             {
124 0         0 $c->stash->{template} = 'user/prefs.tt';
125 0         0 $c->stash->{page_user} = $page_user;
126             }
127             else {
128 0         0 $c->stash->{message} = $c->loc('User not found: x', $login);
129 0         0 $c->stash->{template} = 'message.tt';
130             }
131 35     35   35109 }
  35         93  
  35         169  
132              
133             =head2 prefs ( .prefs )
134              
135             Main user preferences screen.
136              
137             =cut
138              
139              
140             sub prefs : Global FormConfig {
141 0     0 1 0 my ( $self, $c ) = @_;
142 0         0 my $form = $c->stash->{form};
143 0         0 $c->forward('page_user');
144 0         0 my $page_user = $c->stash->{page_user};
145 0         0 $form->model->default_values( $c->stash->{user} );
146 0 0       0 if ( $form->submitted_and_valid ) {
147 0         0 my $old_email = $page_user->email;
148 0         0 $form->model->update($page_user);
149 0         0 $c->stash->{message} = $c->loc('Updated preferences');
150 0 0       0 if ( $form->params->{email} ne $old_email ) {
151 0         0 $page_user->active(-1);
152 0         0 $page_user->update;
153 0         0 $c->forward( 'do_register', [$page_user] );
154             }
155             }
156 35     35   35006 }
  35         91  
  35         162  
157              
158             =head2 password ( .prefs/password )
159              
160             Change password action.
161              
162             B<template:> user/password.tt
163              
164             =cut
165              
166             sub password : Path('/prefs/password') FormConfig {
167 0     0 1 0 my ( $self, $c ) = @_;
168 0         0 $c->forward('page_user');
169 0         0 my $page_user = $c->stash->{page_user};
170 0         0 my $form = $c->stash->{form};
171 0 0       0 if ( $form->submitted_and_valid ) {
172              
173             # FIXME: Should be moved into a formfu validator
174 0 0       0 unless ( $page_user->valid_pass( $form->params->{current} ) ) {
175 0         0 $c->stash->{message} = $c->loc('Invalid password');
176 0         0 return;
177             }
178 0         0 $page_user->pass( $form->params->{pass} );
179 0         0 $page_user->update();
180 0         0 $c->stash->{message} = $c->loc('Your password has been updated');
181             }
182 35     35   34420 }
  35         90  
  35         157  
183              
184             =head2 recover_pass
185              
186             Email a user a new password
187              
188             =cut
189              
190             sub recover_pass : Global {
191 2     2 1 2236 my ( $self, $c ) = @_;
192 2 100       9 return unless ( $c->req->method eq 'POST' );
193 1         51 my $id = $c->req->param('recover');
194 1         190 my $user =
195             $c->model('DBIC::Person')->search( [ email => $id, login => $id ] )
196             ->first;
197 1 50       4298 unless ( $user ) {
198 0         0 $c->flash->{message} = $c->loc('Could not recover password');
199 0         0 return $c->res->redirect( $c->uri_for('login') );
200             }
201              
202             $c->stash(
203             user => $user,
204             password => Text::Password::Pronounceable->generate(6, 10),
205             email => {
206             from => $c->config->{system_mail},
207 1         154 to => $user->login . ' <' . $user->email . '>',
208             subject => $c->loc('Your new password on x', $c->pref('name')),
209             template => 'reset_password.tt',
210             },
211             );
212              
213 1         984 $c->forward( $c->view('Email') );
214 1 50       26055 if ( scalar( @{ $c->error } ) ) {
  1         4  
215 0         0 $c->clear_errors;
216             $c->stash->{message} =
217 0         0 $c->loc('Error occurred while emailing you your new password.');
218             }
219             else {
220 1         18 $user->pass( $c->stash->{password} );
221 1         759 $user->update();
222 1         29200 $c->flash->{message} = $c->loc('Emailed you your new password.');
223 1         4625 return $c->res->redirect( $c->uri_for('login') );
224             }
225 35     35   37655 }
  35         91  
  35         185  
226              
227             =head2 register ( /.register )
228              
229             Show new user registration form.
230              
231             B<template:> C<user/register.tt>
232              
233             =cut
234              
235             sub register : Global FormConfig {
236 2     2 1 542826 my ( $self, $c ) = @_;
237              
238 2 0 0     19 if ( !$c->pref('open_registration')
      33        
239             && ( !( $c->stash->{user} && $c->stash->{user}->is_admin ) ) )
240             {
241 0         0 $c->stash->{template} = 'message.tt';
242 0         0 return $c->stash->{message} = $c->loc('Registration is closed!');
243             }
244              
245 2         371 $c->stash->{template} = 'user/register.tt';
246 2         142 $c->stash->{message} = $c->loc(
247             'Please fill in the following information to register. All fields are mandatory.'
248             );
249 2         1180 my $form = $c->stash->{form};
250 2         130 $c->stash->{newuser} = $c->model('DBIC::Person')->new_result( {} );
251 2         2138 $c->stash->{template} = 'user/register.tt';
252              
253 2 50 0     174 if ( $c->pref('use_captcha')
      33        
254             && ( !( $c->stash->{user} && $c->stash->{user}->is_admin ) ) )
255             {
256 0   0     0 my $captcha_lang = $c->session->{lang} || $c->pref('default_lang');
257 0         0 my $captcha = $form->element({
258             type => 'reCAPTCHA',
259             name => 'captcha',
260             recaptcha_options => { lang => $captcha_lang, theme => 'white' }
261             });
262 0         0 $form->process;
263             }
264              
265 2         424 $form->model->default_values( $c->stash->{newuser} );
266 2 100       39062 if ( $form->submitted_and_valid ) {
267             # Need to check if login or email already exists.
268 1 50       2014 if ( $c->forward('is_account_taken') ) {
269 0         0 $c->stash->{account_taken} = $c->loc('Account Taken. Try another.');
270 0         0 $c->detach();
271             }
272 1         5369 $c->stash->{newuser}->active(-1);
273              
274             # XXX - need to add this so FormFu->model->update properly populates
275             # the required registered field. The other way to do this is by using
276             # DBIx::Class::DynamicDefaults, but I've restrained myself from adding
277             # yet another dependency -lestrrat
278             # $form->add_valid( registered => time() );
279 1         383 $form->model->update( $c->stash->{newuser} );
280 1         31798 $c->stash->{newuser}->insert();
281 1 50 33     270 if ( $c->stash->{user} && $c->stash->{user}->is_admin ) {
282 0         0 $c->stash->{newuser}->update({active=>1});
283 0         0 $c->res->redirect( $c->uri_for('/.admin/user') );
284             }
285             else {
286 1         84 $c->forward( 'do_register', [ $c->stash->{newuser} ] );
287             }
288             }
289 35     35   40526 }
  35         3969  
  35         187  
290              
291             =head2 is_account_taken
292              
293             Test to see if a login or email is already in use.
294              
295             =cut
296              
297             sub is_account_taken : Private {
298 1     1 1 636 my ( $self, $c ) = @_;
299              
300 1         21 my $login = $c->request->body_params->{login};
301 1         57 my $email = $c->request->body_params->{email};
302 1         37 my $person_rs =
303             $c->model('DBIC::Person')
304             ->search( [ { login => $login }, { email => $email } ] );
305              
306 1         796 return $person_rs->count;
307 35     35   33410 }
  35         95  
  35         167  
308              
309             =head2 do_register ( /.register )
310              
311             New user registration processing.
312              
313             B<templates:> C<user/password.tt>, C<user/validate.tt>
314              
315             =cut
316              
317             sub do_register : Private {
318 1     1 1 755 my ( $self, $c, $user ) = @_;
319 1         6 $c->forward('/user/login');
320 1 50       705 $c->pref('entropy') || $c->pref( 'entropy', rand );
321             $c->stash(
322             secret => md5_hex( $user->email . $c->pref('entropy') ),
323             email => {
324             from => $c->config->{system_mail},
325 1         266 to => $user->email,
326             subject => $c->loc( '~[x~] New User Validation', $c->pref('name') ),
327             template => 'validate.tt',
328             },
329             );
330 1         1204 $c->model('DBIC::Page')->create_page($user->link,
331             $c->loc("# Home node for x\n\nPut whatever you like here.",$user->name),
332             $user);
333              
334 1         96 $c->forward( $c->view('Email') );
335 1 50       5337 if ( scalar( @{ $c->error } ) ) {
  1         5  
336 0         0 $c->clear_errors;
337 0         0 $c->stash->{error} = $c->loc('An error occourred. Sorry.');
338             }
339 1         14 $c->stash->{user} = $user;
340 1         65 $c->stash->{template} = 'user/validate.tt';
341 35     35   35793 }
  35         3932  
  35         4073  
342              
343             =head2 validate ( /.validate )
344              
345             Validation of user email. Will accept a md5_hex mailed to the user
346             earlier. Non-validated users will only be able to log out.
347              
348             =cut
349              
350             sub validate : Global {
351 0     0 1 0 my ( $self, $c, $user, $check ) = @_;
352 0         0 $user = $c->model("DBIC::Person")->find( { login => $user } );
353 0 0 0     0 if ( $user and $check eq md5_hex( $user->email . $c->pref('entropy') ) ) {
354 0         0 $user->update({active=>1});
355 0 0       0 if ( $c->stash->{user} ) {
356             $c->res->redirect(
357 0         0 $c->uri_for( '/', $c->stash->{user}->link, '.edit' ) );
358             }
359             else {
360             $c->flash->{message} =
361 0         0 $c->loc( 'Welcome, x your email is validated. Please log in.',
362             $user->name );
363 0         0 return $c->res->redirect( $c->uri_for('login') );
364             }
365 0         0 return;
366             }
367 0         0 $c->stash->{template} = 'user/validate.tt';
368 35     35   42822 }
  35         93  
  35         176  
369              
370             =head2 reconfirm
371              
372             Send the confirmation mail again to another address.
373              
374             =cut
375              
376             sub reconfirm : Local {
377 0     0 1 0 my ( $self, $c ) = @_;
378 0 0       0 $c->detach('/default') unless $c->req->method eq 'POST';
379 0 0       0 if ( $c->user->obj->email ne $c->req->param('email') ) {
380 0 0       0 if ( $c->model('DBIC::Person')
381             ->search( { email => $c->req->param('email') } )->count )
382             {
383 0         0 return $c->stash->{error} = $c->loc('That mail is already in use');
384             }
385             }
386 0         0 my $user = $c->user->obj;
387 0         0 $user->email( $c->req->params->{email} );
388 0         0 $user->active(-1);
389 0         0 $user->update();
390 0         0 $c->forward( 'do_register', [$user] );
391 0         0 $c->flash->{message} = $c->loc('confirmation message resent');
392 0         0 $c->res->redirect( $c->uri_for('/') );
393 35     35   35780 }
  35         93  
  35         174  
394              
395             =head2 profile ( .profile )
396              
397             Show user profile.
398              
399             =cut
400              
401             sub profile : Global {
402 0     0 1 0 my ( $self, $c ) = @_;
403 0         0 my $page = $c->stash->{page};
404             my $login = (
405             $c->stash->{proto_pages}[-1]
406             ? $c->stash->{proto_pages}[-1]->{name} # FIXME: why not ->{name_orig}, like in editprofile() ?
407 0 0       0 : $page->name
408             );
409 0         0 my $user = $c->model('DBIC::Person')->get_user($login);
410 0 0       0 if ($user) {
411 0         0 $c->stash->{person} = $user;
412 0         0 $c->stash->{template} = 'user/profile.tt';
413             }
414             else {
415 0         0 $c->stash->{template} = 'message.tt';
416 0         0 $c->stash->{message} = $c->loc( 'User not found: x', $login );
417             }
418 35     35   34260 }
  35         96  
  35         169  
419              
420             =head2 editprofile
421              
422             Form to edit a person's profile
423              
424             =cut
425              
426             sub editprofile : Global FormConfig {
427 0     0 1 0 my ( $self, $c ) = @_;
428 0         0 my $form = $c->stash->{form};
429 0         0 my $page = $c->stash->{page};
430             my $login = (
431             $c->stash->{proto_pages}[-1]
432             ? $c->stash->{proto_pages}[-1]->{name_orig}
433 0 0       0 : $page->name
434             );
435 0         0 my $user = $c->model('DBIC::Person')->get_user($login);
436 0 0 0     0 if (
      0        
      0        
437             $user
438             && $c->stash->{user}
439             && ( $c->stash->{user}->is_admin
440             || $user->id eq $c->stash->{user}->id )
441             )
442             {
443 0 0       0 if ( $form->submitted_and_valid ) {
444 0         0 $form->model->update($user);
445 0         0 $c->res->redirect( $c->uri_for('profile') );
446             }
447 0 0       0 $form->model->default_values($user) unless $form->submitted;
448             }
449             else {
450 0         0 $c->stash->{template} = 'message.tt';
451 0         0 $c->stash->{message} = $c->loc('User not found: x', $login);
452             }
453              
454 35     35   36036 }
  35         101  
  35         206  
455              
456             =head2 do_editprofile
457              
458             Apply the edits to a person's profile
459              
460             =cut
461              
462             sub do_editprofile : Global {
463 0     0 1   my ( $self, $c ) = @_;
464 0           $c->form(
465             required => [qw(name email)],
466             optional => [ $c->model("DBIC::Person")->result_source->columns ],
467             defaults => { gender => undef },
468             constraint_methods =>
469             { born => ymd_to_datetime(qw(birth_year birth_month birth_day)) },
470             untaint_all_constraints => 1,
471             );
472              
473 0 0         if ( $c->form->has_missing ) {
    0          
474             $c->stash->{message} =
475 0           $c->loc('You have to fill in all required fields.')
476             . $c->loc('the following are missing:') . ' <b>'
477             . join( ', ', $c->form->missing() ) . '</b>';
478             }
479             elsif ( $c->form->has_invalid ) {
480 0           $c->stash->{message} = $c->loc(
481             'Some fields are invalid. Please correct them and try again:');
482             }
483             else {
484 0           my $page = $c->stash->{page};
485             my $user = $c->model('DBIC::Person')->get_user(
486             $c->stash->{proto_pages}[-1]
487             ? $c->stash->{proto_pages}[-1]->{name_orig}
488 0 0         : $page->name
489             );
490 0           $user->set_columns( $c->form->{valid} );
491 0           $user->update();
492 0           return $c->forward('profile');
493             }
494 0           $c->forward('editprofile');
495 35     35   37440 }
  35         154  
  35         3991  
496              
497             =head1 AUTHOR
498              
499             David Naughton <naughton@cpan.org>,
500             Marcus Ramberg <mramberg@cpan.org>
501              
502             =head1 LICENSE
503              
504             This library is free software. You can redistribute it and/or modify
505             it under the same terms as Perl itself.
506              
507             =cut
508              
509             1;