File Coverage

blib/lib/Plack/App/Login/Password.pm
Criterion Covered Total %
statement 50 80 62.5
branch 6 20 30.0
condition 1 12 8.3
subroutine 14 16 87.5
pod n/a
total 71 128 55.4


line stmt bran cond sub pod time code
1             package Plack::App::Login::Password;
2              
3 4     4   157170 use base qw(Plack::Component::Tags::HTML);
  4         30  
  4         2192  
4 4     4   297915 use strict;
  4         12  
  4         91  
5 4     4   21 use warnings;
  4         10  
  4         104  
6              
7 4     4   2222 use Plack::Request;
  4         237204  
  4         145  
8 4     4   1843 use Plack::Response;
  4         4946  
  4         145  
9 4     4   1758 use Plack::Session;
  4         2045  
  4         144  
10 4         22 use Plack::Util::Accessor qw(generator login_cb logo_image_url message_cb redirect_login redirect_error
11 4     4   49 register_link title);
  4         10  
12 4     4   2271 use Tags::HTML::Container;
  4         4889  
  4         129  
13 4     4   2047 use Tags::HTML::Login::Access;
  4         11728  
  4         2956  
14              
15             our $VERSION = 0.02;
16              
17             sub _css {
18 3     3   891 my ($self, $env) = @_;
19              
20 3         14 $self->{'_container'}->process_css;
21 3         787 $self->{'_login_access'}->process_css({
22             'error' => 'red',
23             'info' => 'blue',
24             });
25              
26 3         7086 return;
27             }
28              
29             sub _login_check {
30 0     0   0 my ($self, $env, $body_parameters_hr) = @_;
31              
32 0 0 0     0 if (! exists $body_parameters_hr->{'login'}
33             || $body_parameters_hr->{'login'} ne 'login') {
34              
35 0         0 $self->_message($env, 'error', 'There is no login POST.');
36 0         0 return 0;
37             }
38 0 0 0     0 if (! defined $body_parameters_hr->{'username'} || ! $body_parameters_hr->{'username'}) {
39 0         0 $self->_message($env, 'error', "Missing username.");
40 0         0 return 0;
41             }
42 0 0 0     0 if (! defined $body_parameters_hr->{'password'} || ! $body_parameters_hr->{'password'}) {
43 0         0 $self->_message($env, 'error', "Missing password.");
44 0         0 return 0;
45             }
46              
47 0         0 return 1;
48             }
49             sub _message {
50 0     0   0 my ($self, $env, $message_type, $message) = @_;
51              
52 0 0       0 if (defined $self->message_cb) {
53 0         0 $self->message_cb->($env, $message_type, $message);
54             }
55              
56 0         0 return;
57             }
58              
59             sub _prepare_app {
60 5     5   34471 my $self = shift;
61              
62             # Defaults which rewrite defaults in module which I am inheriting.
63 5 100       20 if (! defined $self->generator) {
64 2         70 $self->generator(__PACKAGE__.'; Version: '.$VERSION);
65             }
66              
67 5 100       42 if (! defined $self->title) {
68 4         25 $self->title('Login page');
69             }
70              
71             # Inherite defaults.
72 5         42 $self->SUPER::_prepare_app;
73              
74             # Defaults from this module.
75 3         1921 my %p = (
76             'css' => $self->css,
77             'tags' => $self->tags,
78             );
79              
80             # Tags helper for login button.
81 3         48 $self->{'_login_access'} = Tags::HTML::Login::Access->new(
82             %p,
83             'logo_image_url' => $self->logo_image_url,
84             'register_url' => $self->register_link,
85             );
86              
87 3         943 $self->{'_container'} = Tags::HTML::Container->new(%p);
88              
89 3         454 return;
90             }
91              
92             sub _process_actions {
93 3     3   46 my ($self, $env) = @_;
94              
95 3 50 33     8 if (defined $self->login_cb && $env->{'REQUEST_METHOD'} eq 'POST') {
96 0         0 my $req = Plack::Request->new($env);
97 0         0 my $body_params_hr = $req->body_parameters;
98 0         0 my ($status, $messages_ar) = $self->_login_check($env, $body_params_hr);
99 0         0 my $res = Plack::Response->new;
100 0 0       0 if ($status) {
101 0 0       0 if ($self->login_cb->($env, $body_params_hr->{'username'},
102             $body_params_hr->{'password'})) {
103              
104 0         0 $self->_message($env, 'info',
105             "User '$body_params_hr->{'username'}' is logged.");
106 0         0 $res->redirect($self->redirect_login);
107             } else {
108 0         0 $self->_message($env, 'error', 'Bad login.');
109 0         0 $res->redirect($self->redirect_error);
110             }
111             } else {
112 0         0 $res->redirect($self->redirect_error);
113             }
114 0         0 $self->psgi_app($res->finalize);
115             }
116              
117 3         21 return;
118             }
119              
120             sub _tags_middle {
121 3     3   13543 my ($self, $env) = @_;
122              
123 3         7 my $messages_ar = [];
124 3 50       11 if (exists $env->{'psgix.session'}) {
125 0         0 my $session = Plack::Session->new($env);
126 0         0 $messages_ar = $session->get('messages');
127 0         0 $session->set('messages', []);
128             }
129             $self->{'_container'}->process(
130             sub {
131 3     3   2095 $self->{'_login_access'}->process($messages_ar);
132             },
133 3         22 );
134              
135 3         22846 return;
136             }
137              
138             1;
139              
140             __END__
141              
142             =pod
143              
144             =encoding utf8
145              
146             =head1 NAME
147              
148             Plack::App::Login::Password - Plack login/password application.
149              
150             =head1 SYNOPSIS
151              
152             use Plack::App::Login::Password;
153              
154             my $obj = Plack::App::Login::Password->new(%parameters);
155             my $psgi_ar = $obj->call($env);
156             my $app = $obj->to_app;
157              
158             =head1 METHODS
159              
160             =head2 C<new>
161              
162             my $obj = Plack::App::Login::Password->new(%parameters);
163              
164             Constructor.
165              
166             Returns instance of object.
167              
168             =over 8
169              
170             =item * C<author>
171              
172             Author string to HTML head.
173              
174             Default value is undef.
175              
176             =item * C<content_type>
177              
178             Content type for output.
179              
180             Default value is 'text/html; charset=__ENCODING__'.
181              
182             =item * C<css>
183              
184             Instance of CSS::Struct::Output object.
185              
186             Default value is CSS::Struct::Output::Raw instance.
187              
188             =item * C<css_init>
189              
190             Reference to array with CSS::Struct structure.
191              
192             Default value is CSS initialization from Tags::HTML::Page::Begin like
193              
194             * {
195             box-sizing: border-box;
196             margin: 0;
197             padding: 0;
198             }
199              
200             =item * C<encoding>
201              
202             Set encoding for output.
203              
204             Default value is 'utf-8'.
205              
206             =item * C<favicon>
207              
208             Link to favicon.
209              
210             Default value is undef.
211              
212             =item * C<flag_begin>
213              
214             Flag that means begin of html writing via L<Tags::HTML::Page::Begin>.
215              
216             Default value is 1.
217              
218             =item * C<flag_end>
219              
220             Flag that means end of html writing via L<Tags::HTML::Page::End>.
221             Default value is 1.
222              
223             =item * C<generator>
224              
225             HTML generator string.
226              
227             Default value is 'Plack::App::Login; Version: __VERSION__'.
228              
229             =item * C<login_cb>
230              
231             Callback for main login.
232             Arguments for callback are: C<$env>, C<username> and C<$password>.
233             Returns 0/1 for (un)successful login.
234              
235             Default value is undef.
236              
237             =item * C<logo_image_url>
238              
239             URL to logo image.
240              
241             Default value is undef.
242              
243             =item * C<message_cb>
244              
245             Callback to process message from application.
246             Arguments for callback are: C<$env>, C<$message_type> and C<$message>.
247             Returns undef.
248              
249             Default value is undef.
250              
251             =item * C<psgi_app>
252              
253             PSGI application to run instead of normal process.
254             Intent of this is change application in C<_process_actions> method.
255              
256             Default value is undef.
257              
258             =item * C<redirect_login>
259              
260             Redirect URL after successful login.
261              
262             Default value is undef.
263              
264             =item * C<redirect_error>
265              
266             Redirect URL after error in login.
267              
268             Default value is undef.
269              
270             =item * C<register_link>
271              
272             URL to registering page.
273              
274             Default value is undef.
275              
276             =item * C<script_js>
277              
278             Reference to array with Javascript code strings.
279              
280             Default value is [].
281              
282             =item * C<script_js_src>
283              
284             Reference to array with Javascript URLs.
285              
286             Default value is [].
287              
288             =item * C<status_code>
289              
290             HTTP status code.
291              
292             Default value is 200.
293              
294             =item * C<tags>
295              
296             Instance of Tags::Output object.
297              
298             Default value is
299              
300             Tags::Output::Raw->new(
301             'xml' => 1,
302             'no_simple' => ['script', 'textarea'],
303             'preserved' => ['pre', 'style'],
304             );
305              
306             =item * C<title>
307              
308             Page title.
309              
310             Default value is 'Login page'.
311              
312             =back
313              
314             =head2 C<call>
315              
316             my $psgi_ar = $obj->call($env);
317              
318             Implementation of login page.
319              
320             Returns reference to array (PSGI structure).
321              
322             =head2 C<to_app>
323              
324             my $app = $obj->to_app;
325              
326             Creates Plack application.
327              
328             Returns Plack::Component object.
329              
330             =head1 EXAMPLE
331              
332             =for comment filename=login_password_psgi.pl
333              
334             use strict;
335             use warnings;
336              
337             use CSS::Struct::Output::Indent;
338             use Plack::App::Login::Password;
339             use Plack::Runner;
340             use Tags::Output::Indent;
341              
342             # Run application.
343             my $app = Plack::App::Login::Password->new(
344             'css' => CSS::Struct::Output::Indent->new,
345             'generator' => 'Plack::App::Login::Password',
346             'tags' => Tags::Output::Indent->new(
347             'preserved' => ['style'],
348             'xml' => 1,
349             ),
350             )->to_app;
351             Plack::Runner->new->run($app);
352              
353             # Output:
354             # HTTP::Server::PSGI: Accepting connections at http://0:5000/
355              
356             # > curl http://localhost:5000/
357             # <!DOCTYPE html>
358             # <html lang="en">
359             # <head>
360             # <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
361             # <meta name="generator" content="Plack::App::Login::Password"
362             # />
363             # <meta name="viewport" content="width=device-width, initial-scale=1.0" />
364             # <title>
365             # Login page
366             # </title>
367             # <style type="text/css">
368             # * {
369             # box-sizing: border-box;
370             # margin: 0;
371             # padding: 0;
372             # }
373             # .container {
374             # display: flex;
375             # align-items: center;
376             # justify-content: center;
377             # height: 100vh;
378             # }
379             # .form-login {
380             # width: 300px;
381             # background-color: #f2f2f2;
382             # padding: 20px;
383             # border-radius: 5px;
384             # box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
385             # }
386             # .form-login fieldset {
387             # border: none;
388             # padding: 0;
389             # margin-bottom: 20px;
390             # }
391             # .form-login legend {
392             # font-weight: bold;
393             # margin-bottom: 10px;
394             # }
395             # .form-login p {
396             # margin: 0;
397             # padding: 10px 0;
398             # }
399             # .form-login label {
400             # display: block;
401             # font-weight: bold;
402             # margin-bottom: 5px;
403             # }
404             # .form-login input[type="text"], .form-login input[type="password"] {
405             # width: 100%;
406             # padding: 8px;
407             # border: 1px solid #ccc;
408             # border-radius: 3px;
409             # }
410             # .form-login button[type="submit"] {
411             # width: 100%;
412             # padding: 10px;
413             # background-color: #4CAF50;
414             # color: #fff;
415             # border: none;
416             # border-radius: 3px;
417             # cursor: pointer;
418             # }
419             # .form-login button[type="submit"]:hover {
420             # background-color: #45a049;
421             # }
422             # </style>
423             # </head>
424             # <body>
425             # <div class="container">
426             # <div class="inner">
427             # <form class="form-login" method="post">
428             # <fieldset>
429             # <legend>
430             # Login
431             # </legend>
432             # <p>
433             # <label for="username" />
434             # User name
435             # <input type="text" name="username" id="username" />
436             # </p>
437             # <p>
438             # <label for="password">
439             # Password
440             # </label>
441             # <input type="password" name="password" id="password" />
442             # </p>
443             # <p>
444             # <button type="submit" name="login" value="login">
445             # Login
446             # </button>
447             # </p>
448             # </fieldset>
449             # </form>
450             # </div>
451             # </div>
452             # </body>
453             # </html>
454              
455             =head1 DEPENDENCIES
456              
457             L<Plack::Component::Tags::HTML>,
458             L<Plack::Request>,
459             L<Plack::Response>,
460             L<Plack::Session>,
461             L<Plack::Util::Accessor>,
462             L<Tags::HTML::Container>,
463             L<Tags::HTML::Login::Password>.
464              
465             =head1 SEE ALSO
466              
467             =over
468              
469             =item L<Plack::App::Login>
470              
471             Plack login application.
472              
473             =back
474              
475             =head1 REPOSITORY
476              
477             L<https://github.com/michal-josef-spacek/Plack-App-Login-Password>
478              
479             =head1 AUTHOR
480              
481             Michal Josef Špaček L<mailto:skim@cpan.org>
482              
483             L<http://skim.cz>
484              
485             =head1 LICENSE AND COPYRIGHT
486              
487             © 2023 Michal Josef Špaček
488              
489             BSD 2-Clause License
490              
491             =head1 VERSION
492              
493             0.02
494              
495             =cut