File Coverage

blib/lib/Dancer2/Plugin/Auth/YARBAC.pm
Criterion Covered Total %
statement 37 417 8.8
branch 0 32 0.0
condition 0 27 0.0
subroutine 10 63 15.8
pod n/a
total 47 539 8.7


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::Auth::YARBAC;
2              
3 6     6   3739122 use strict;
  6         14  
  6         176  
4 6     6   30 use warnings;
  6         14  
  6         167  
5              
6 6     6   31 use Dancer2::Plugin;
  6         9  
  6         47  
7 6     6   18333 use Module::Load;
  6         6655  
  6         39  
8 6     6   274 use Carp;
  6         12  
  6         443  
9 6     6   31 use Try::Tiny;
  6         12  
  6         386  
10 6     6   32 use Data::Dumper;
  6         71  
  6         32538  
11              
12             our $VERSION = '0.011';
13              
14             register logged_in_user => sub
15             {
16 0     0   0 my $dsl = shift;
17 0         0 my $app = $dsl->app;
18 0         0 my $conf = plugin_setting();
19              
20 0 0 0     0 if ( $app->session->read('logged_in_user') && $app->session->read('logged_in_user_realm') )
21             {
22 0         0 return $dsl->retrieve_user;
23             }
24              
25 0         0 return;
26             };
27              
28             register hook_before_require_login => sub
29             {
30 6     6   275187 my $dsl = shift;
31 6         19 my $coderef = shift;
32 6         46 my $app = $dsl->app;
33 6         72 my $conf = plugin_setting();
34              
35 6         987 return _require_login( $dsl, $coderef, $conf, 'hook' );
36             };
37              
38             register require_login => sub
39             {
40 6     6   59 my $dsl = shift;
41 6         14 my $coderef = shift;
42 6         27 my $app = $dsl->app;
43 6         165 my $conf = plugin_setting();
44              
45 6         450 return _require_login( $dsl, $coderef, $conf );
46             };
47              
48             register logout => sub
49             {
50 0     0   0 my $dsl = shift;
51 0         0 my $app = $dsl->app;
52 0         0 my $conf = plugin_setting();
53              
54 0         0 $app->destroy_session;
55              
56 0         0 return $app->redirect( $conf->{after_logout} );
57             };
58              
59             register login => sub
60             {
61 0     0   0 my $dsl = shift;
62 0         0 my $params = shift;
63 0         0 my $opts = shift;
64 0         0 my $app = $dsl->app;
65 0         0 my $conf = plugin_setting();
66 0         0 my $auth = authenticate_user( $dsl, $params, $opts );
67              
68 0 0 0     0 if ( $auth->{success} && $auth->{realm} )
69             {
70 0         0 $app->session->write( logged_in_user => $params->{username} );
71 0         0 $app->session->write( logged_in_user_realm => $auth->{realm} );
72 0         0 $app->session->delete('login_failed');
73              
74 0         0 my $return_url = $app->session->read('return_url');
75              
76 0 0       0 return $dsl->redirect( ( $return_url ) ? $return_url : $dsl->uri_for( $conf->{after_login} ) );
77             }
78              
79 0         0 my $login_failed = $app->session->read( 'login_failed' );
80              
81 0         0 $app->session->write( login_failed => ++$login_failed );
82 0         0 $dsl->debug( 'YARBAC ========> Setting login_failed: ' . $login_failed . ' times.' );
83              
84 0         0 return $dsl->redirect( $dsl->uri_for( $conf->{login_denied} ) . '?login_failed=' . $login_failed );
85             };
86              
87             register authenticate_user => sub
88             {
89 0     0   0 my $dsl = shift;
90 0         0 my $params = shift;
91 0         0 my $opts = shift;
92 0         0 my $app = $dsl->app;
93 0         0 my $conf = plugin_setting();
94 0         0 my $auth = 0;
95              
96 0 0       0 if ( ! $opts->{realm} )
97             {
98 0         0 for my $try_realm ( keys %{ $conf->{realms} } )
  0         0  
99             {
100 0         0 $opts->{realm} = $try_realm;
101 0         0 $auth = _try_auth_realm( $dsl, $params, $opts );
102 0 0       0 last if ( $auth );
103             }
104             }
105             else
106             {
107 0         0 $auth = _try_auth_realm( $dsl, $params, $opts );
108             }
109              
110 0         0 return { success => $auth, realm => $opts->{realm} };
111             };
112              
113             register generate_hash => sub
114             {
115 0     0   0 my $dsl = shift;
116 0         0 my $params = shift;
117 0         0 my $opts = shift;
118 0         0 my $app = $dsl->app;
119 0         0 my $conf = plugin_setting();
120              
121 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
122              
123 0         0 return $provider->generate_hash( $params, $opts );
124             };
125              
126             register password_strength => sub
127             {
128 0     0   0 my $dsl = shift;
129 0         0 my $params = shift;
130 0         0 my $opts = shift;
131 0         0 my $app = $dsl->app;
132 0         0 my $conf = plugin_setting();
133              
134 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
135 0         0 my $strength = $provider->password_strength( $params, $opts );
136              
137 0 0       0 if ( $strength->{error} )
138             {
139 0         0 for my $error ( @{ $strength->{errors} } )
  0         0  
140             {
141 0         0 $dsl->debug( "YARBAC ========> Password had the following error: error code is '$error->{code}' error message is '$error->{message}'" );
142             }
143             }
144              
145 0         0 return $strength;
146             };
147              
148             register retrieve_user => sub
149             {
150 0     0   0 my $dsl = shift;
151 0         0 my $params = _try_username( $dsl->app, shift );
152 0         0 my $opts = shift;
153 0         0 my $app = $dsl->app;
154 0         0 my $conf = plugin_setting();
155              
156 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
157              
158 0         0 return $provider->retrieve_user( $params, $opts );
159             };
160              
161             register retrieve_role => sub
162             {
163 0     0   0 my $dsl = shift;
164 0         0 my $params = shift;
165 0         0 my $opts = shift;
166 0         0 my $app = $dsl->app;
167 0         0 my $conf = plugin_setting();
168              
169 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
170              
171 0         0 return $provider->retrieve_role( $params, $opts );
172             };
173              
174             register retrieve_group => sub
175             {
176 0     0   0 my $dsl = shift;
177 0         0 my $params = shift;
178 0         0 my $opts = shift;
179 0         0 my $app = $dsl->app;
180 0         0 my $conf = plugin_setting();
181              
182 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
183              
184 0         0 return $provider->retrieve_group( $params, $opts );
185             };
186              
187             register retrieve_permission => sub
188             {
189 0     0   0 my $dsl = shift;
190 0         0 my $params = shift;
191 0         0 my $opts = shift;
192 0         0 my $app = $dsl->app;
193 0         0 my $conf = plugin_setting();
194            
195 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
196              
197 0         0 return $provider->retrieve_permission( $params, $opts );
198             };
199              
200             register user_roles => sub
201             {
202 0     0   0 my $dsl = shift;
203 0         0 my $params = _try_username( $dsl->app, shift );
204 0         0 my $opts = shift;
205 0         0 my $app = $dsl->app;
206 0         0 my $conf = plugin_setting();
207              
208 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
209              
210 0         0 return $provider->user_roles( $params, $opts );
211             };
212              
213             register user_groups => sub
214             {
215 0     0   0 my $dsl = shift;
216 0         0 my $params = _try_username( $dsl->app, shift );
217 0         0 my $opts = shift;
218 0         0 my $app = $dsl->app;
219 0         0 my $conf = plugin_setting();
220              
221 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
222              
223 0         0 return $provider->user_groups( $params, $opts );
224             };
225              
226             register user_has_role => sub
227             {
228 0     0   0 my $dsl = shift;
229 0         0 my $params = _try_username( $dsl->app, shift );
230 0         0 my $opts = shift;
231 0         0 my $app = $dsl->app;
232 0         0 my $conf = plugin_setting();
233              
234 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
235              
236 0         0 return $provider->user_has_role( $params, $opts );
237             };
238              
239             register user_has_any_role => sub
240             {
241 0     0   0 my $dsl = shift;
242 0         0 my $params = _try_username( $dsl->app, shift );
243 0         0 my $opts = shift;
244 0         0 my $app = $dsl->app;
245 0         0 my $conf = plugin_setting();
246              
247 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
248              
249 0         0 return $provider->user_has_any_role( $params, $opts );
250             };
251              
252             register user_has_all_roles => sub
253             {
254 0     0   0 my $dsl = shift;
255 0         0 my $params = _try_username( $dsl->app, shift );
256 0         0 my $opts = shift;
257 0         0 my $app = $dsl->app;
258 0         0 my $conf = plugin_setting();
259              
260 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
261              
262 0         0 return $provider->user_has_all_roles( $params, $opts );
263             };
264              
265             register user_has_group => sub
266             {
267 0     0   0 my $dsl = shift;
268 0         0 my $params = _try_username( $dsl->app, shift );
269 0         0 my $opts = shift;
270 0         0 my $app = $dsl->app;
271 0         0 my $conf = plugin_setting();
272              
273 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
274              
275 0         0 return $provider->user_has_group( $params, $opts );
276             };
277              
278             register user_has_any_group => sub
279             {
280 0     0   0 my $dsl = shift;
281 0         0 my $params = _try_username( $dsl->app, shift );
282 0         0 my $opts = shift;
283 0         0 my $app = $dsl->app;
284 0         0 my $conf = plugin_setting();
285              
286 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
287              
288 0         0 return $provider->user_has_any_group( $params, $opts );
289             };
290              
291             register user_has_all_groups => sub
292             {
293 0     0   0 my $dsl = shift;
294 0         0 my $params = _try_username( $dsl->app, shift );
295 0         0 my $opts = shift;
296 0         0 my $app = $dsl->app;
297 0         0 my $conf = plugin_setting();
298              
299 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
300              
301 0         0 return $provider->user_has_all_groups( $params, $opts );
302             };
303              
304             register user_has_group_permission => sub
305             {
306 0     0   0 my $dsl = shift;
307 0         0 my $params = _try_username( $dsl->app, shift );
308 0         0 my $opts = shift;
309 0         0 my $app = $dsl->app;
310 0         0 my $conf = plugin_setting();
311              
312 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
313              
314 0         0 return $provider->user_has_group_permission( $params, $opts );
315             };
316              
317             register user_has_group_with_any_permission => sub
318             {
319 0     0   0 my $dsl = shift;
320 0         0 my $params = _try_username( $dsl->app, shift );
321 0         0 my $opts = shift;
322 0         0 my $app = $dsl->app;
323 0         0 my $conf = plugin_setting();
324              
325 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
326              
327 0         0 return $provider->user_has_group_with_any_permission( $params, $opts );
328             };
329              
330             register user_has_group_with_all_permissions => sub
331             {
332 0     0   0 my $dsl = shift;
333 0         0 my $params = _try_username( $dsl->app, shift );
334 0         0 my $opts = shift;
335 0         0 my $app = $dsl->app;
336 0         0 my $conf = plugin_setting();
337              
338 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
339              
340 0         0 return $provider->user_has_group_with_all_permissions( $params, $opts );
341             };
342              
343             register role_has_group => sub
344             {
345 0     0   0 my $dsl = shift;
346 0         0 my $params = shift;
347 0         0 my $opts = shift;
348 0         0 my $app = $dsl->app;
349 0         0 my $conf = plugin_setting();
350              
351 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
352              
353 0         0 return $provider->role_has_group( $params, $opts );
354             };
355              
356             register role_groups => sub
357             {
358 0     0   0 my $dsl = shift;
359 0         0 my $params = shift;
360 0         0 my $opts = shift;
361 0         0 my $app = $dsl->app;
362 0         0 my $conf = plugin_setting();
363              
364 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
365              
366 0         0 return $provider->role_groups( $params, $opts );
367             };
368              
369             register group_permissions => sub
370             {
371 0     0   0 my $dsl = shift;
372 0         0 my $params = shift;
373 0         0 my $opts = shift;
374 0         0 my $app = $dsl->app;
375 0         0 my $conf = plugin_setting();
376              
377 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
378              
379 0         0 return $provider->group_permissions( $params, $opts );
380             };
381              
382             register group_has_permission => sub
383             {
384 0     0   0 my $dsl = shift;
385 0         0 my $params = shift;
386 0         0 my $opts = shift;
387 0         0 my $app = $dsl->app;
388 0         0 my $conf = plugin_setting();
389              
390 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
391              
392 0         0 return $provider->group_has_permission( $params, $opts );
393             };
394              
395             register create_user => sub
396             {
397 0     0   0 my $dsl = shift;
398 0         0 my $params = shift;
399 0         0 my $opts = shift;
400 0         0 my $app = $dsl->app;
401 0         0 my $conf = plugin_setting();
402              
403 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
404              
405 0         0 return $provider->create_user( $params, $opts );
406             };
407              
408             register create_role => sub
409             {
410 0     0   0 my $dsl = shift;
411 0         0 my $params = shift;
412 0         0 my $opts = shift;
413 0         0 my $app = $dsl->app;
414 0         0 my $conf = plugin_setting();
415              
416 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
417              
418 0         0 return $provider->create_role( $params, $opts );
419             };
420              
421             register create_group => sub
422             {
423 0     0   0 my $dsl = shift;
424 0         0 my $params = shift;
425 0         0 my $opts = shift;
426 0         0 my $app = $dsl->app;
427 0         0 my $conf = plugin_setting();
428              
429 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
430              
431 0         0 return $provider->create_group( $params, $opts );
432             };
433              
434             register create_permission => sub
435             {
436 0     0   0 my $dsl = shift;
437 0         0 my $params = shift;
438 0         0 my $opts = shift;
439 0         0 my $app = $dsl->app;
440 0         0 my $conf = plugin_setting();
441              
442 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
443            
444 0         0 return $provider->create_permission( $params, $opts );
445             };
446              
447             register assign_user_role => sub
448             {
449 0     0   0 my $dsl = shift;
450 0         0 my $params = shift;
451 0         0 my $opts = shift;
452 0         0 my $app = $dsl->app;
453 0         0 my $conf = plugin_setting();
454              
455 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
456              
457 0         0 return $provider->assign_user_role( $params, $opts );
458             };
459              
460             register assign_role_group => sub
461             {
462 0     0   0 my $dsl = shift;
463 0         0 my $params = shift;
464 0         0 my $opts = shift;
465 0         0 my $app = $dsl->app;
466 0         0 my $conf = plugin_setting();
467              
468 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
469              
470 0         0 return $provider->assign_role_group( $params, $opts );
471             };
472              
473             register assign_group_permission => sub
474             {
475 0     0   0 my $dsl = shift;
476 0         0 my $params = shift;
477 0         0 my $opts = shift;
478 0         0 my $app = $dsl->app;
479 0         0 my $conf = plugin_setting();
480              
481 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
482              
483 0         0 return $provider->assign_group_permission( $params, $opts );
484             };
485              
486             register revoke_user_role => sub
487             {
488 0     0   0 my $dsl = shift;
489 0         0 my $params = shift;
490 0         0 my $opts = shift;
491 0         0 my $app = $dsl->app;
492 0         0 my $conf = plugin_setting();
493              
494 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
495              
496 0         0 return $provider->revoke_user_role( $params, $opts );
497             };
498              
499             register revoke_role_group => sub
500             {
501 0     0   0 my $dsl = shift;
502 0         0 my $params = shift;
503 0         0 my $opts = shift;
504 0         0 my $app = $dsl->app;
505 0         0 my $conf = plugin_setting();
506              
507 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
508            
509 0         0 return $provider->revoke_role_group( $params, $opts );
510             };
511              
512             register revoke_group_permission => sub
513             {
514 0     0   0 my $dsl = shift;
515 0         0 my $params = shift;
516 0         0 my $opts = shift;
517 0         0 my $app = $dsl->app;
518 0         0 my $conf = plugin_setting();
519              
520 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
521              
522 0         0 return $provider->revoke_group_permission( $params, $opts );
523             };
524              
525             register modify_user => sub
526             {
527 0     0   0 my $dsl = shift;
528 0         0 my $params = shift;
529 0         0 my $opts = shift;
530 0         0 my $app = $dsl->app;
531 0         0 my $conf = plugin_setting();
532              
533 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
534              
535 0         0 return $provider->modify_user( $params, $opts );
536             };
537              
538             register modify_role => sub
539             {
540 0     0   0 my $dsl = shift;
541 0         0 my $params = shift;
542 0         0 my $opts = shift;
543 0         0 my $app = $dsl->app;
544 0         0 my $conf = plugin_setting();
545              
546 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
547              
548 0         0 return $provider->modify_role( $params, $opts );
549             };
550              
551             register modify_group => sub
552             {
553 0     0   0 my $dsl = shift;
554 0         0 my $params = shift;
555 0         0 my $opts = shift;
556 0         0 my $app = $dsl->app;
557 0         0 my $conf = plugin_setting();
558              
559 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
560              
561 0         0 return $provider->modify_group( $params, $opts );
562             };
563              
564             register modify_permission => sub
565             {
566 0     0   0 my $dsl = shift;
567 0         0 my $params = shift;
568 0         0 my $opts = shift;
569 0         0 my $app = $dsl->app;
570 0         0 my $conf = plugin_setting();
571              
572 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
573              
574 0         0 return $provider->modify_permission( $params, $opts );
575             };
576              
577             register delete_user => sub
578             {
579 0     0   0 my $dsl = shift;
580 0         0 my $params = shift;
581 0         0 my $opts = shift;
582 0         0 my $app = $dsl->app;
583 0         0 my $conf = plugin_setting();
584              
585 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
586              
587 0         0 return $provider->delete_user( $params, $opts );
588             };
589              
590             register delete_role => sub
591             {
592 0     0   0 my $dsl = shift;
593 0         0 my $params = shift;
594 0         0 my $opts = shift;
595 0         0 my $app = $dsl->app;
596 0         0 my $conf = plugin_setting();
597              
598 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
599            
600 0         0 return $provider->delete_role( $params, $opts );
601             };
602              
603             register delete_group => sub
604             {
605 0     0   0 my $dsl = shift;
606 0         0 my $params = shift;
607 0         0 my $opts = shift;
608 0         0 my $app = $dsl->app;
609 0         0 my $conf = plugin_setting();
610              
611 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
612              
613 0         0 return $provider->delete_group( $params, $opts );
614             };
615              
616             register delete_permission => sub
617             {
618 0     0   0 my $dsl = shift;
619 0         0 my $params = shift;
620 0         0 my $opts = shift;
621 0         0 my $app = $dsl->app;
622 0         0 my $conf = plugin_setting();
623              
624 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
625              
626 0         0 return $provider->delete_permission( $params, $opts );
627             };
628              
629             register provider => sub
630             {
631 0     0   0 my $dsl = shift;
632 0         0 my $params = shift;
633 0         0 my $opts = shift;
634 0         0 my $app = $dsl->app;
635 0         0 my $conf = plugin_setting();
636              
637 0         0 my $provider = _provider( $dsl, $conf, _try_realm( $app, $opts ) );
638              
639 0         0 return $provider;
640             };
641              
642             sub _require_login
643             {
644 12     12   29 my $dsl = shift;
645 12         23 my $coderef = shift;
646 12         21 my $conf = shift;
647 12         23 my $hook = shift;
648 12         36 my $app = $dsl->app;
649              
650             return sub
651             {
652 0 0 0 0     if ( ! $coderef || ref $coderef ne 'CODE' )
653             {
654 0           croak "Invalid require_login usage, please see documentation.";
655             }
656              
657 0 0 0       if ( ! $app->session->read('logged_in_user') || ! $app->session->read('logged_in_user_realm') )
658             {
659 0 0 0       if ( defined $hook && $dsl->request->path_info =~ m/$conf->{no_login_required}/i )
660             {
661 0           return $coderef->();
662             }
663              
664 0           $dsl->debug( 'YARBAC ========> User is not authenticated.' );
665              
666 0 0         if ( $dsl->request->path_info !~ m/^($conf->{login_denied}|$conf->{after_login})$/i )
667             {
668 0           $dsl->debug( 'YARBAC ========> Setting return_url: ' . $dsl->request->uri_base . $dsl->request->request_uri );
669 0           $app->session->write( return_url => $dsl->request->uri_base . $dsl->request->request_uri );
670             }
671              
672 0           return $dsl->redirect( $dsl->uri_for( $conf->{login_denied} ) );
673             }
674              
675 0           return $coderef->();
676 12         110 };
677             }
678              
679             sub _try_auth_realm
680             {
681 0     0     my $dsl = shift;
682 0           my $params = shift;
683 0           my $opts = shift;
684 0           my $app = $dsl->app;
685 0           my $conf = plugin_setting();
686              
687 0 0 0       return if ( ! defined $params->{username} || ! defined $params->{password} || ! defined $opts->{realm} );
      0        
688              
689 0           $dsl->debug( "YARBAC ========> Attempting to authenticate $params->{username} against realm $opts->{realm}." );
690              
691 0           my $provider = _provider( $dsl, $conf, $opts );
692              
693 0 0         if ( $provider->authenticate_user( $params, $opts ) )
694             {
695 0           $dsl->debug( "YARBAC ========> Realm $opts->{realm} accepted user $params->{username}." );
696              
697 0           return 1;
698             }
699              
700 0           return;
701             }
702              
703             sub _try_realm
704             {
705 0     0     my $app = shift;
706 0           my $opts = shift;
707              
708 0 0         if ( ! defined $opts->{realm} )
709             {
710 0           $opts->{realm} = $app->session->read('logged_in_user_realm');
711             }
712              
713 0           return $opts;
714             }
715              
716             sub _try_username
717             {
718 0     0     my $app = shift;
719 0           my $params = shift;
720              
721 0 0         if ( ! defined $params->{username} )
722             {
723 0           $params->{username} = $app->session->read('logged_in_user');
724             }
725              
726 0           return $params;
727             }
728              
729             {
730             my $provider = {};
731              
732             sub _provider
733             {
734 0     0     my $dsl = shift;
735 0           my $app = $dsl->app;
736 0           my $conf = shift;
737 0           my $opts = shift;
738              
739 0 0         return $provider->{ $opts->{realm} } if exists $provider->{ $opts->{realm} };
740              
741 0   0       my $settings = $conf->{realms}{ $opts->{realm} } || croak "Unknown realm $opts->{realm}";
742 0   0       my $class = $settings->{provider} || croak "No provider configured see: " . __PACKAGE__;
743              
744 0 0         if ( $class !~ m{::} )
745             {
746 0           $class = __PACKAGE__ . '::Provider::' . $settings->{provider};
747             }
748              
749             try
750             {
751 0     0     load $class;
752             }
753             catch
754             {
755 0     0     croak "Failed to load provider: $class $_";
756 0           };
757              
758 0           $dsl->debug( 'YARBAC ========> Loaded provider: ' . $class );
759              
760 0           $provider->{ $opts->{realm} } = $class->new( dsl => $dsl, app => $app, settings => $settings );
761              
762 0           my $require_methods = [ 'authenticate_user', 'retrieve_user', 'retrieve_role', 'retrieve_group',
763             'retrieve_permission', 'user_roles', 'user_groups', 'user_has_role',
764             'user_has_group_permission', 'role_has_group', 'group_permissions',
765             'group_has_permission', 'create_user', 'create_role', 'create_group',
766             'create_permission', 'assign_user_role', 'assign_role_group',
767             'assign_group_permission', 'revoke_user_role', 'revoke_role_group',
768             'revoke_group_permission', 'delete_user', 'delete_role', 'delete_group',
769             'delete_permission', 'modify_user', 'modify_role', 'modify_group',
770             'modify_permission', 'user_has_group', 'user_has_any_group',
771             'user_has_all_groups', 'user_has_group_with_any_permission',
772             'user_has_group_with_all_permissions', 'user_has_any_role',
773             'user_has_all_roles', 'role_groups',
774             ];
775              
776 0           for my $method ( @{ $require_methods } )
  0            
777             {
778             try
779             {
780 0     0     $provider->{ $opts->{realm} }->$method();
781             }
782             catch
783             {
784 0     0     croak "Provider $class does not contain the required method: $method.";
785 0           };
786             }
787              
788 0           return $provider->{ $opts->{realm} };
789             }
790             }
791              
792             register_plugin for_versions => [ 2 ] ;
793              
794             1;
795              
796             =pod
797              
798             =encoding UTF-8
799              
800             =head1 NAME
801              
802             Dancer2::Plugin::Auth::YARBAC - Yet Another Role Based Access Control Framework
803              
804             =head1 VERSION
805              
806             version 0.011
807              
808             =head1 SYNOPSIS
809              
810             Configure the plugin to use the authentication provider class you would like to use.
811             In this example we'll use the 'Database' provider:
812              
813             plugins:
814             Auth::YARBAC:
815             # Set redirect page after user logs out
816             after_logout: '/login'
817             # Set default redirect page after user logs in
818             after_login: '/'
819             # Set default redirect page if user fails login attempt
820             login_denied: '/login'
821             # Specify URL's that do not require authentication
822             no_login_required: '^/login|/denied|/css|/images|/generate_hash'
823             # Set your realms, one realm is required but you can have many
824             realms:
825             # Realm name
826             test:
827             # Our backend provider
828             provider: 'Database'
829             # Set the users table name (required by Database, default: users)
830             users_table: 'users'
831             # Set the users id column name (required by Database, default: id)
832             users_id_column: 'id'
833             # Set the users username column name (Database, default: username)
834             users_username_column: 'username'
835             # Set the users username column name (Database, default: password)
836             users_password_column: 'password'
837             # Password strength options optionally allows a check password strength
838             password_strength:
839             # Set the required minimum password score
840             required_score: 25
841             # Set minimum password length
842             min_length: 6
843             # Set maximum password length (good idea to avoid DDOS attacks)
844             max_length: 32
845             # If true, password must contain special characters
846             special_characters: 1
847             # If true, password must contain control characters
848             control_characters: 1
849             # If true, password must not be a repeating character
850             no_repeating: 1
851             # If true, password must contain a uppercase character
852             upper_case: 1
853             # If true, password must contain a lowercase character
854             lower_case: 1
855             # If true, password must contain a number
856             numbers: 1
857              
858             In your app the order of modules loaded is important. Ensure you set
859             the session module before YARBAC. YARBAC doesn't care which session
860             module you use so long as one is loaded.
861             If you're using the L<Dancer2::Plugin::Auth::YARBAC::Provider::Database>
862             backend provider ensure you've also loaded L<Dancer2::Plugin::Database>
863             before YARBAC.
864              
865             package MyPackage;
866            
867             use Dancer2;
868             use Dancer2::Plugin::Database;
869             use Dancer2::Session::Cookie;
870             use Dancer2::Plugin::Auth::YARBAC;
871              
872             The configuration you provide will depend on the backend provider module
873             you choose to use. In this example we're assuming the Database
874             backend provider, see:
875             L<Dancer2::Plugin::Auth::YARBAC::Provider::Database>.
876              
877             =head1 DESCRIPTION
878              
879             YARBAC is a role based user authentication and authorisation framework for Dancer2 apps.
880             Designed with security and a medium to large user base in mind.
881             This framework was heavily inspired by the excellent L<Dancer::Plugin::Auth::Extensible>
882             framework which I'd highly recommend.
883             YARBAC was designed to support secure password checking, enforced password hashing,
884             multiple authentication realms and the ability to create your own backend provider.
885             YARBAC was also designed to to be as flexible and as feature rich as possible
886             in the hope that I'll never have to write RBAC code for Dancer again. :)
887             While similar to Extensible in some ways, this framework has some significantly
888             different approaches.
889             These differences were born out of my own experiences writing RBAC code for various
890             Dancer apps and finding myself always having to extend existing modules or
891             starting from scratch or worse still, copy/paste my old code then reworking it.
892             The major difference with YARBAC is that it tries to be a complete solution to the problem.
893             However in order to be a little more flexible and feature rich in some areas it is also
894             a little more opinionated in others.
895             The main area of opinion in YARBAC is how it achieves role-based access control.
896             YARBAC is structed with users, roles, groups and permissions.
897             A user can have many roles but it might be a good idea in larger enviornments to only allow
898             a user to have one role and then assign that role have many groups. Think of a
899             role as being a role-group. Then there are groups which have many permissions.
900             A user can have one or more roles, a role can have one or more groups and groups
901             can have one or more permissions.
902             This means when deciding if a user is authorised we could require they be logged in, or have
903             a specifc role, or specific group, or a specific group with a specific permission and so on.
904             To put it another way, this design moves the access control down to the role-group
905             relationship thus allowing one to quickly and easily see, assign or revoke permissions to a
906             user even when dealing with a fairly complex authorisation environment.
907              
908             The logical flow of this design looks like so:
909              
910             +------------+
911             -->| PERMISSION |
912             +-------+ | +------------+
913             -->| GROUP |----
914             +------+ | +-------+ +------------+
915             -->| ROLE |---- -->| PERMISSION |
916             | +------+ | +-------+ | +------------+
917             | -->| GROUP |----
918             | +-------+ | +------------+
919             | -->| PERMISSION |
920             +------+ | +------------+
921             | USER |----|
922             +------+ | +------------+
923             | -->| PERMISSION |
924             | +-------+ | +------------+
925             | -->| GROUP |----
926             | +------+ | +-------+ | +------------+
927             -->| ROLE |---- -->| PERMISSION |
928             +------+ | +-------+ +------------+
929             -->| GROUP |----
930             +-------+ | +------------+
931             -->| PERMISSION |
932             +------------+
933              
934             Of course just because there are users, roles, groups and permissions doesn't mean
935             you have to use them. This module will happily function even if you just care
936             about user authentication. Or perhaps you're just interested in users and roles,
937             this is also fine.
938              
939             =head1 AUTHENTICATION BACKEND PROVIDERS
940              
941             This framework allows the use of different backend providers.
942             At time of writing only the Database backend is available.
943              
944             =over 4
945              
946             =item L<Dancer2::Plugin::Auth::YARBAC::Provider::Database>
947              
948             Authentication and authorisation using a database backend.
949              
950             =back
951              
952             Want to create your own provider backend? No problem,
953             just write a Moo based module (or similar oo) and use it to extend
954             L<Dancer2::Plugin::Auth::YARBAC::Provider::Base>
955             then implement the required methods and you're done.
956              
957             =head1 CONTROLLING AUTHENTICATION ACCESS
958              
959             There are three ways you can control authentication access to your app.
960             One is using the keyword 'hook_before_require_login' which is a global
961             check for all routes.
962             This is handy if your app is mostly locked down with only a few exceptions.
963             The exceptions can be specified in your apps config using the option
964             'no_login_required' and putting in exempt routes here as a regex.
965             The second option is to use the keyword 'require_login' which must be
966             set on each route you wish authentication to be a requirement.
967             This is handy when most of your app is open to the big wide
968             world but you've got a few routes that need protecting.
969             The third option is the keyword 'logged_in_user' which is
970             more manual but handy if the default behavour of URL
971             redirecting is getting in your way.
972              
973             =over
974              
975             =item hook_before_require_login - Add on 'hook before', requires user to be logged in
976              
977             hook before => hook_before_require_login sub {
978            
979             };
980              
981             If the user attempts to access a route that's not exempt via the config
982             option regex 'no_login_require' then they will be redirected
983             to whatever URL was specified in the apps config using the option
984             'login_denied'.
985              
986             =item require_login - Add to any route, requires user to be logged in
987              
988             get '/auth/is/required' => require_login sub {
989            
990             };
991              
992             If the user attempts to access this route and they are not logged
993             in they'll be redirected to whatever URL was specified
994             in the apps config using the option 'login_denied'.
995              
996             =item logged_in_user - Checks if user is logged in.
997              
998             get '/' => sub {
999             unless ( logged_in_user ) {
1000             # user isn't logged in.
1001             }
1002              
1003             template 'index', {};
1004             };
1005              
1006             If the user is logged in, the keyword 'logged_in_user'
1007             will return the logged in user as a hashref.
1008             If the user is not logged in, it returns false.
1009              
1010             =back
1011              
1012             =head1 GRANTING AUTHENTICATION ACCESS
1013              
1014             There are two ways one can authenticate a user.
1015             The first is using the built in 'login' keyword.
1016             This option will take care of the session management
1017             for you and redirect the user. Please note,
1018             YARBAC requires you to use a Dancer2 session module
1019             in your app but it doesn't care which one you choose.
1020             The second is using the keyword 'authenticate_user'
1021             which will only check if the username and password
1022             was correct and then report back with a hashref.
1023              
1024             =over
1025              
1026             =item login - Attempts login and then redirects.
1027              
1028             any ['get', 'post'] => '/login' => sub {
1029             # Optionally set the realm:
1030             # return login( { username => params->{username},
1031             # password => params->{password} },
1032             # { realm => params->{realm} } );
1033            
1034             if ( params->{username} && params->{password} ) {
1035             return login( { username => params->{username},
1036             password => params->{password} } );
1037             }
1038            
1039             template 'login', {};
1040             };
1041              
1042             If the user authenticates successfully this will redirect the user to whatever
1043             was set via the config option 'after_login'. Unless the user was trying
1044             to access a specific route prior to authentication, in which case
1045             this will redirect the user to whatever route the user was trying to access.
1046             If the users attempt to authenticate fails this will redirect the user to
1047             whatever was set via the config option 'login_denied'.
1048             This also keeps track of the amount of failed authentication
1049             attempts by the user with the 'login_failed' param.
1050              
1051             =item authenticate_user - Attempts auth and then returns a hashref
1052              
1053             get '/auth/user' => sub {
1054             # Optionally set the realm:
1055             # my $auth = authenticate_user( { username => params->{username},
1056             # password => params->{password} },
1057             # { realm => params->{realm} } );
1058              
1059             my $auth = authenticate_user( { username => params->{username},
1060             password => params->{password} } );
1061              
1062             unless ( $auth->{success} ) {
1063             # User did not provide valid username or password.
1064             }
1065              
1066             #.......
1067             };
1068              
1069             If the user authenticates successfully this will return a hashref with
1070             $auth->{success} being true and $auth->{realm} which will contain the
1071             realm name the user authenticated againts successfully.
1072             If the users attempt to authenticate fails this will return a hashref but
1073             $auth->{realm} will be false.
1074             That is all this keyword does so in order for a user to be correctly
1075             authenticated with YARBAC the session name 'logged_in_user' will
1076             need to be set with the users username and 'logged_in_user_realm'
1077             set with the users realm name.
1078              
1079             =back
1080              
1081             =head1 CONTROLLING AUTHORISATION BASED ACCESS
1082              
1083             As previously stated, a user can have many roles, a role has one or
1084             more groups and a group has one or more permissions.
1085             Therefore we can determine if a user should be granted access to
1086             a route or other material based on a number of requirements.
1087              
1088             =over
1089              
1090             =item user_has_role - Checks if a user has a role
1091              
1092             get '/has/role' => sub {
1093             # Optionally check user other than current logged in user:
1094             # user_has_role( { role_name => 'admin', username => 'sarah' } );
1095             #
1096             # Optionally user other realm than current logged in user:
1097             # user_has_role( { role_name => 'admin', }, { realm => 'admins' } );
1098              
1099             my $has_role = user_has_role( { role_name => 'admin' } );
1100              
1101             unless ( $has_role ) {
1102             # User doesn't have the role.
1103             }
1104            
1105             #.......
1106             };
1107              
1108             Keyword 'user_has_role' will check if the current logged in user
1109             has the role specified. However one can specify a user to check by
1110             adding 'username'. One can also check a different realm by
1111             adding 'realm'. If the user has the role returns true otherwiese
1112             returns false.
1113              
1114             =item user_has_any_role - Checks an arrayref of role names to see if the user has any
1115              
1116             get '/has/any/role' => sub {
1117             # Optionally check user other than current logged in user:
1118             # user_has_any_role( { role_names => [ 'admin', 'managers' ], username => 'sarah' } );
1119             #
1120             # Optionally user other realm than current logged in user:
1121             # user_has_any_role( { role_names => [ 'admin', 'managers' ] }, { realm => 'admins' } );
1122              
1123             my $has_role = user_has_any_role( { role_names => [ 'admin', 'managers' ] } );
1124              
1125             unless ( $has_role ) {
1126             # User doesn't have the role.
1127             }
1128            
1129             #.......
1130             };
1131              
1132             Keyword 'user_has_any_role' will check if the current logged in user
1133             has any of the roles specified. However one can specify a user to check by
1134             adding 'username'. One can also check a different realm by
1135             adding 'realm'. If the user has any of the roles returns true otherwise
1136             returns false.
1137              
1138             =item user_has_all_roles - Checks an arrayref of role names to see if the user has all
1139              
1140             get '/has/all/roles' => sub {
1141             # Optionally check user other than current logged in user:
1142             # user_has_all_roles( { role_names => [ 'admin', 'managers' ],
1143             # username => 'gabby' } );
1144             #
1145             # Optionally use other realm than current logged in user:
1146             # user_has_all_roles( { role_names => [ 'admin', 'managers' ] },
1147             # { realm => 'admins' } );
1148              
1149             my $has_roles = user_has_all_roles( { role_names => [ 'admin', 'managers' ] } );
1150              
1151             unless ( $has_roles ) {
1152             # User doesn't have all of these roles.
1153             }
1154              
1155             #.......
1156             };
1157              
1158             Keyword 'user_has_all_roles' will check if the current logged in user
1159             has all of the roles specified. However one can specify a user to check by
1160             adding 'username'. One can also check a different realm by
1161             adding 'realm'. If the user has all of the roles returns true otherwise
1162             returns false.
1163              
1164             =item user_has_group - Checks if a user has a group
1165              
1166             get '/has/group' => sub {
1167             # Optionally check user other than current logged in user:
1168             # user_has_group( { group_name => 'cs', username => 'ada' } );
1169             #
1170             # Optionally use other realm than current logged in user:
1171             # user_has_group( { group_name => 'cs'}, { realm => 'admins' } );
1172              
1173             my $has_group = user_has_group( { group_name => 'cs' } );
1174              
1175             unless ( $has_group ) {
1176             # User doesn't have the group.
1177             }
1178            
1179             #.......
1180             };
1181              
1182             Keyword 'user_has_group' will check if the current logged in user
1183             has the group specified. However one can specify a user to check by
1184             adding 'username'. One can also check a different realm by
1185             adding 'realm'. If the user has the group returns true otherwise
1186             returns false.
1187              
1188             =item user_has_any_group - Checks an arrayref of group names to see if the user has any
1189              
1190             get '/has/any/group' => sub {
1191             # Optionally check user other than current logged in user:
1192             # user_has_any_group( { group_names => [ 'cs', 'ops' ], username => 'morris' } );
1193             #
1194             # Optionally use other realm than current logged in user:
1195             # user_has_any_group( { group_names => [ 'cs', 'ops' ] }, { realm => 'admins' } );
1196              
1197             my $has_groups = user_has_any_group( { group_names => [ 'cs', 'ops' ] } );
1198              
1199             unless ( $has_groups ) {
1200             # User doesn't have any of these groups.
1201             }
1202              
1203             #.......
1204             };
1205              
1206             Keyword 'user_has_any_group' will check if the current logged in user
1207             has any of the groups specified. However one can specify a user to check by
1208             adding 'username'. One can also check a different realm by
1209             adding 'realm'. If the user has any of the groups returns true otherwise
1210             returns false.
1211              
1212             =item user_has_all_groups - Checks an arrayref of group names to see if the user has all
1213              
1214             get '/has/all/groups' => sub {
1215             # Optionally check user other than current logged in user:
1216             # user_has_all_groups( { group_names => [ 'cs', 'ops' ],
1217             # username => 'gabby' } );
1218             #
1219             # Optionally use other realm than current logged in user:
1220             # user_has_all_groups( { group_names => [ 'cs', 'ops' ] },
1221             # { realm => 'admins' } );
1222              
1223             my $has_groups = user_has_all_groups( { group_names => [ 'cs', 'ops' ] } );
1224              
1225             unless ( $has_groups ) {
1226             # User doesn't have all of these groups.
1227             }
1228              
1229             #.......
1230             };
1231              
1232             Keyword 'user_has_all_groups' will check if the current logged in user
1233             has all of the groups specified. However one can specify a user to check by
1234             adding 'username'. One can also check a different realm by
1235             adding 'realm'. If the user has all of the groups returns true otherwise
1236             returns false.
1237              
1238             =item user_has_group_permission - Checks if a user has a specific group with a permission
1239              
1240             get '/has/group/permission' => sub {
1241             # Optionally check user other than current logged in user:
1242             # user_has_group_permission( { group_name => 'cs',
1243             permission_name => 'write',
1244             username => 'Tess' } );
1245             #
1246             # Optionally use other realm than current logged in user:
1247             # user_has_group_permission( { group_name => 'cs',
1248             permission_name => 'write' },
1249             { realm => 'admins' } );
1250              
1251             my $has_permission = user_has_group_permission( { group_name => 'cs',
1252             permission_name => 'write' } );
1253              
1254             unless ( $has_permission ) {
1255             # User doesn't have this group or group doesn't have this permission.
1256             }
1257              
1258             #.......
1259             };
1260              
1261             Keyword 'user_has_group_permission' will check if the current logged in user
1262             has specified group and that this group has a specified permission.
1263             However one can specify a user to check by adding 'username'.
1264             One can also check a different realm by adding 'realm'.
1265             If the user has all of the groups returns true otherwise returns false.
1266              
1267             =item user_has_group_with_any_permission - Checks if a user has a specific group with any permission
1268              
1269             get '/has/group/with/any/permission' => sub {
1270             # Optionally check user other than current logged in user:
1271             # user_has_group_with_any_permission( { group_name => 'cs',
1272             # permission_names => [ 'write', 'delete' ],
1273             # username => 'Jane' } );
1274             #
1275             # Optionally use other realm than current logged in user:
1276             # user_has_group_with_any_permission( { group_name => 'cs',
1277             # permission_names => [ 'write', 'delete' ] },
1278             # { realm => 'admins' } );
1279              
1280             my $has_permission = user_has_group_with_any_permission( group_name => 'cs',
1281             permission_names => [ 'write', 'delete' ] );
1282              
1283             unless ( $has_permission ) {
1284             # User doesn't have this group or group doesn't have any of these permissions.
1285             }
1286              
1287             #.......
1288             };
1289              
1290             Keyword 'user_has_group_with_any_permission' will check if the current logged in user
1291             has the specified group and that this group has any of the specified permissions.
1292             However one can specify a user to check by adding 'username'.
1293             One can also check a different realm by adding 'realm'.
1294             If the user has the group with any of the specified permissions returns true
1295             otherwise returns false.
1296              
1297             =item user_has_group_with_all_permissions - Checks if a user has a specific group with all permissions
1298              
1299             get '/has/group/with/all/permissions' => sub {
1300             # Optionally check user other than current logged in user:
1301             # user_has_group_with_all_permissions( { group_name => 'cs', permission_names => [ 'write', 'delete' ],
1302             # username => 'matthew' } );
1303             #
1304             # Optionally use other realm than current logged in user:
1305             # user_has_group_with_all_permissions( { group_name => 'cs', permission_names => [ 'write', 'delete' ] },
1306             # { realm => 'admins' } );
1307              
1308             my $has_permission = user_has_group_with_all_permissions( { group_name => 'cs',
1309             permission_names => [ 'write', 'delete' ] } );
1310              
1311             unless ( $has_permission ) {
1312             # User doesn't have this group or group doesn't have all of these permissions.
1313             }
1314              
1315             #.......
1316             };
1317              
1318             Keyword 'user_has_group_with_all_permissions' will check if the current logged in user
1319             has the specified group and that this group has all of the specified permissions.
1320             However one can specify a user to check by adding 'username'.
1321             One can also check a different realm by adding 'realm'.
1322             If the user has the group with all of the specified permissions returns true
1323             otherwise returns false.
1324              
1325             =item role_has_group - Checks if role has a group
1326              
1327             get '/role/has/group' => sub {
1328             # Optionally use other realm than current logged in user:
1329             # role_has_group( { role_name => 'admin',
1330             # group_name => 'ops' },
1331             # { realm => 'admins' } );
1332              
1333             my $has_group = role_has_group( { role_name => 'admin',
1334             group_name => 'ops' } );
1335              
1336             unless( $has_group ) {
1337             # Role doesn't have this group.
1338             }
1339              
1340             #.......
1341             };
1342              
1343             Keyword 'role_has_group' will check if the specified role
1344             has the group specified.
1345             One can also check a different realm by adding 'realm'.
1346             If the role has the group returns true otherwise
1347             returns false.
1348              
1349             =item group_has_permission - Checks if group has a permission
1350              
1351             get '/group/has/permission' => sub {
1352             # Optionally use other realm than current logged in user:
1353             # group_has_permission( { group_name => 'cs',
1354             # permission_name => 'write' },
1355             # { realm => 'admins' } );
1356              
1357             my $has_permission = group_has_permission( { group_name => 'cs',
1358             permission_name => 'write' } );
1359            
1360             unless {
1361             # group does not have this permission.
1362             }
1363              
1364             #.......
1365             };
1366              
1367             Keyword 'group_has_permission' will check if the specified group
1368             has the permission specified.
1369             One can also check a different realm by adding 'realm'.
1370             If the group has the permission returns true otherwise
1371             returns false.
1372              
1373             =back
1374              
1375             =head1 CREATING USERS, ROLES, GROUPS & PERMISSIONS
1376              
1377             =over
1378              
1379             =item create_user - Creates a user
1380              
1381             get '/create/user' => sub {
1382             # Optionally use other realm than current logged in user:
1383             # my $create = create_user( { username => 'Craig',
1384             # password => 'pass' },
1385             # { realm => 'admins' } );
1386             # Oh, and don't ever use that password for real, obviously :)
1387              
1388             my $create = create_user( { username => 'Craig',
1389             password => 'pass' } );
1390             if ( $create ) {
1391             # success
1392             }
1393              
1394             #.......
1395             };
1396              
1397             Keyword 'create_user' creates a new user. So long as your
1398             backend can match the hash key (i.e Database backend has a
1399             column name that matches the hash key) the data will be accepted.
1400             The users password is always hashed by default.
1401             One can also use a different realm by adding 'realm'.
1402             If the user was created returns true otherwise reutrns false.
1403              
1404             =item create_role - Creates a role
1405              
1406             get '/create/role' => sub {
1407             # Optionally use other realm than current logged in user:
1408             # my $create = create_role( { role_name => 'some role',
1409             # description => 'blah blah' },
1410             # { realm => 'admins' } );
1411             #
1412             # description is optional
1413             my $create = create_role( { role_name => 'some role',
1414             description => 'blah blah' } );
1415             if ( $create ) {
1416             # success
1417             }
1418              
1419             #.......
1420             };
1421              
1422             Keyword 'create_role' creates a new role.
1423             One can also use a different realm by adding 'realm'.
1424             One can also optionally give the role a description.
1425             If the role was created returns true otherwise reutrns false.
1426              
1427             =item create_group - Creates a group
1428              
1429             get '/create/group' => sub {
1430             # Optionally use other realm than current logged in user:
1431             # $create = create_group( { group_name => params->{group},
1432             # description => params->{description} },
1433             # { realm => 'admins' } );
1434             #
1435             # description is optional
1436             my $create = create_group( { group_name => params->{group},
1437             description => params->{description} } );
1438             if ( $create ) {
1439             # success
1440             }
1441              
1442             #.......
1443             };
1444              
1445             Keyword 'create_group' creates a new group.
1446             One can also use a different realm by adding 'realm'.
1447             One can also optionally give the group a description.
1448             If the group was created returns true otherwise reutrns false.
1449              
1450             =item create_permission - Creates a permission
1451              
1452             get '/create/permission' => sub {
1453             # Optionally use other realm than current logged in user:
1454             # $create = create_permission( { permission_name => 'write',
1455             # description => 'write stuff' },
1456             # { realm => 'admins' } );
1457             #
1458             # description is optional
1459             my $create = create_permission( { permission_name => 'write',
1460             description => 'write stuff' } );
1461             if ( $create ) {
1462             # success
1463             }
1464              
1465             #.......
1466             };
1467              
1468             Keyword 'create_permission' creates a new permission.
1469             One can also use a different realm by adding 'realm'.
1470             One can also optionally give the group a description.
1471             If the permission was created returns true otherwise reutrns false.
1472              
1473             =back
1474              
1475             =head1 ASSIGN ROLES, GROUPS & PERMISSIONS
1476              
1477             =over
1478              
1479             =item assign_user_role - Assign user a role
1480              
1481             get '/assign/user/role' => sub {
1482             # Optionally use other realm than current logged in user:
1483             # my $assign = assign_role_group( { role_name => 'admin',
1484             # group_name => 'ops' },
1485             # { realm => 'admins' } );
1486              
1487             my $assign = assign_user_role( { username => 'klaus',
1488             role_name => 'admin' } );
1489             if ( $assign ) {
1490             # success
1491             }
1492              
1493             #.......
1494             };
1495              
1496             Keyword 'assign_user_role' assigns the user a role.
1497             One can also use a different realm by adding 'realm'.
1498             If the role was assigned returns true otherwise reutrns false.
1499              
1500             =item assign_role_group - Assign role a group
1501              
1502             get '/assign/role/group' => sub {
1503             # Optionally use other realm than current logged in user:
1504             # my $assign = assign_role_group( { role_name => 'admin',
1505             # group_name => 'ops' },
1506             # { realm => 'admins' } );
1507              
1508             my $assign = assign_role_group( { role_name => 'admin',
1509             group_name => 'ops' } );
1510             if ( $assign ) {
1511             # success
1512             }
1513              
1514             #.......
1515             };
1516              
1517             Keyword 'assign_role_group' assigns the role a group.
1518             One can also use a different realm by adding 'realm'.
1519             If the group was assigned returns true otherwise reutrns false.
1520              
1521             =item assign_group_permission
1522              
1523             get '/assign/group/permission' => sub {
1524             # Optionally use other realm than current logged in user:
1525             # my $assign = assign_group_permission( { group_name => 'ops',
1526             # permission_name => 'delete' },
1527             # { realm => 'admins' } );
1528              
1529             my $assign = assign_group_permission( { group_name => 'ops',
1530             permission_name => 'delete' } );
1531             if ( $assign ) {
1532             # success
1533             }
1534              
1535             #.......
1536             };
1537              
1538             Keyword 'assign_group_permission' assigns the group a permission.
1539             One can also use a different realm by adding 'realm'.
1540             If the group was assigned returns true otherwise reutrns false.
1541              
1542             =back
1543              
1544             =head1 RETRIEVING ROLES, GROUPS & PERMISSIONS
1545              
1546             =over
1547              
1548             =item retrieve_user - Returns user as hashref
1549              
1550             get '/user' => sub {
1551             # Optionally use other realm than current logged in user:
1552             # my $user = retrieve_user( { username => params->{username} }, { realm => 'admin' } );
1553             my $user = retrieve_user( { username => params->{username} } );
1554            
1555             # Optionally you can expand the YARBAC authorisation tree to give you all of the
1556             # users roles, groups and permissions the user has displayed in a hierarchical way like so:
1557             my $user = retrieve_user( { username => params->{username} }, { expand => 1 } );
1558            
1559             # This will add the hash key name 'yarbac' to your user hashref which has the tree
1560             # data which will look like:
1561             # $VAR1 = {
1562             # 'username' => 'sarah',
1563             # 'yarbac' => { 'roles' => [ { role_name => 'role', groups => [ { group_name => 'group', permissions => [{}] }, ] }, ] },
1564             # };
1565             # Although the expand call is a bit on the expensive side but can be helpful under certain conditions.
1566             #.......
1567             };
1568              
1569             Keyword 'retrieve_user' returns the user with all attributes
1570             has a hashref if the user exists. If the user does not exist
1571             returns false.
1572              
1573             =item retrieve_role - Returns role as hashref
1574              
1575             get '/role' => sub {
1576             # Optionally use other realm than current logged in user:
1577             # my $role = retrieve_role( { role_name => 'admin' }, { realm => 'admin' } );
1578            
1579             my $role = retrieve_role( { role_name => 'admin' } );
1580            
1581             #.......
1582             };
1583              
1584             Keyword 'retrieve_role' returns the role with all attributes
1585             has a hashref if the role exists. If the role does not exist
1586             returns false.
1587              
1588             =item retrieve_group - Returns group as hashref
1589              
1590             get '/group' => sub {
1591             # Optionally use other realm than current logged in user:
1592             # my $group = retrieve_group( { group_name => 'cs' }, { realm => 'admin' } );
1593            
1594             my $group = retrieve_role( { group_name => 'cs' } );
1595            
1596             #.......
1597             };
1598              
1599             Keyword 'retrieve_group' returns the group with all attributes
1600             has a hashref if the group exists. If the group does not exist
1601             returns false.
1602              
1603             =item retrieve_permission - Returns permission as hashref
1604              
1605             get '/permission' => sub {
1606             # Optionally use other realm than current logged in user:
1607             # my $perms = retrieve_permission( { permission_name => 'write' }, { realm => 'admin' } );
1608            
1609             my $perms = retrieve_permission( { permission_name => 'write' } );
1610            
1611             #.......
1612             };
1613              
1614             Keyword 'retrieve_permission' returns the permission with all attributes
1615             has a hashref if the permission exists. If the permission does not exist
1616             returns false.
1617              
1618             =item role_groups - Returns role groups as arrayref
1619              
1620             get '/role/groups' => sub {
1621             # Optionally use other realm than current logged in user:
1622             # my $groups = role_groups( { role_name => params->{role} }, { realm => 'admin' } );
1623            
1624             my $groups = role_groups( { role_name => params->{role} } );
1625            
1626             #.......
1627             };
1628              
1629             Keyword 'role_groups' returns the all of the role groups as an arrayref.
1630             If the no groups exist returns false.
1631              
1632             =item group_permissions - Returns group permissions as arrayref
1633              
1634             get '/group/permissions' => sub {
1635             # Optionally use other realm than current logged in user:
1636             # my $perms = group_permissions( { group_name => params->{group} }, { realm => 'admin' } );
1637            
1638             my $perms = group_permissions( { group_name => params->{group} }, { realm => 'admin' } );
1639              
1640             #.......
1641             };
1642              
1643             Keyword 'group_permissions' returns the all of the group permissions as an arrayref.
1644             If the no permissions exist returns false.
1645              
1646             =item user_roles - Returns user roles as arrayref
1647              
1648             get '/user/roles' => sub {
1649             # Optionally use other realm than current logged in user:
1650             # my $role = user_roles( { username => params->{username} }, { realm => 'admin' } );
1651            
1652             my $role = user_roles( { username => params->{username} }, { realm => 'admin' } );
1653            
1654             #.......
1655             };
1656              
1657             Keyword 'user_roles' returns the all of the users roles as an arrayref.
1658             If the no roles exist returns false.
1659              
1660             =item user_groups - Returns user groups as arrayref
1661              
1662             get '/user/groups' => sub {
1663             # Optionally use other realm than current logged in user:
1664             # my $groups = user_groups( { username => params->{username} }, { realm => 'admin' } );
1665            
1666             my $groups = user_groups( { username => params->{username} } );
1667            
1668             #.......
1669             };
1670              
1671             Keyword 'user_groups' returns the all of the users groups as an arrayref.
1672             If the no groups exist returns false.
1673              
1674             =back
1675              
1676             =head1 REVOKING ROLES, GROUPS & PERMISSIONS
1677              
1678             =over
1679              
1680             =item revoke_user_role - Revoke a role from a user
1681              
1682             get '/revoke/user/role' => sub {
1683             # Optionally use other realm than current logged in user:
1684             # revoke_user_role( { username => 'sam', role_name => 'admin' }, { realm => 'admins' } );
1685             #
1686             # Optionally use other realm than current logged in user:
1687             my $revoke = revoke_user_role( { username => 'sam', role_name => 'admin' } );
1688            
1689             if ( $revoke ) {
1690             # success
1691             }
1692              
1693             #.......
1694             };
1695              
1696             Keyword 'revoke_user_role' revokes the role from the user.
1697             One can also use a different realm by adding 'realm'.
1698             If the role was revoked returns true otherwise reutrns false.
1699              
1700             =item revoke_role_group - Revoke a group from a role
1701              
1702             get '/revoke/role/group' => sub {
1703             # Optionally use other realm than current logged in user:
1704             # my $revoke = revoke_role_group( { role_name => 'admin',
1705             # group_name => 'marketing' },
1706             # { realm => 'admins' } );
1707              
1708             my $revoke = revoke_role_group( { role_name => 'admin',
1709             group_name => 'marketing' } );
1710            
1711             if ( $revoke ) {
1712             # success
1713             }
1714              
1715             #.......
1716             };
1717              
1718             Keyword 'revoke_role_group' revokes the group from the role.
1719             One can also use a different realm by adding 'realm'.
1720             If the group was revoked returns true otherwise reutrns false.
1721              
1722             =item revoke_group_permission - Revoke a permission from a group
1723              
1724             get '/revoke/group/permission' => sub {
1725             # Optionally use other realm than current logged in user:
1726             # my $revoke = revoke_group_permission( { group_name => 'cs',
1727             # permission_name => 'write' },
1728             # { realm => 'admins' } );
1729              
1730             my $revoke = revoke_group_permission( { group_name => 'cs',
1731             permission_name => 'write' } );
1732            
1733             if ( $revoke ) {
1734             # success
1735             }
1736              
1737             #.......
1738             };
1739              
1740             Keyword 'revoke_group_permission' revokes the permission from the group
1741             One can also use a different realm by adding 'realm'.
1742             If the permssion was revoked returns true otherwise reutrns false.
1743              
1744             =back
1745              
1746             =head1 MODIFYING USERS, ROLES, GROUPS & PERMISSIONS
1747              
1748             =over
1749              
1750             =item modify_user - Modify existing user
1751              
1752             get '/modify/user' => sub {
1753             # Optionally use other realm than current logged in user:
1754             # my $modify = modify_user( { username => 'sarah',
1755             # password => 'my new pass' },
1756             # { id => '1', realm =>'admins' } );
1757             # password is optional
1758             my $modify = modify_user( { username => 'sarah',
1759             password => 'my new pass' },
1760             { id => '1' } );
1761             # This is annoying backwards when compared to
1762             # Dancer::Plugin::Database, sorry about that.
1763             # One could argue this isn't necessary.
1764            
1765             if ( $modify ) {
1766             # success
1767             }
1768              
1769             #.......
1770             };
1771              
1772             Keyword 'modify_user' modifies an existing user. So long as your
1773             backend can match the hash key (i.e Database backend has a
1774             column name that matches the hash key) the data will be accepted.
1775             The users password is optional but is always hashed by default.
1776             One can also use a different realm by adding 'realm'.
1777             If you wish to modify the users username then you'll need to
1778             provide the user id else you can just provide the username or id.
1779             If the user was modified returns true otherwise reutrns false.
1780              
1781             =item modify_role - Modify existing role
1782              
1783             get '/modify/role' => sub {
1784             # Optionally use other realm than current logged in user:
1785             # my $modify = modify_role( { role_name => 'admin',
1786             # description => 'blah' },
1787             # { id => '1', realm => 'admins' } );
1788             #
1789             # description is optional
1790             my $modify = modify_role( { role_name => 'admin',
1791             description => 'blah' },
1792             { id => '1' } );
1793             # This is annoying backwards when compared to
1794             # Dancer::Plugin::Database, sorry about that.
1795             # One could argue this isn't necessary.
1796            
1797             if ( $modify ) {
1798             # success
1799             }
1800              
1801             #.......
1802             };
1803              
1804             Keyword 'modify_role' modifies an existing role.
1805             One can also use a different realm by adding 'realm'.
1806             If you wish to modify the role name then you'll need to
1807             provide the role id else you just provide the role name or id.
1808             One can also optionally give the role a description.
1809             If the role was created returns true otherwise reutrns false.
1810              
1811             =item modify_group - Modify existing group
1812              
1813             get '/modify/group' => sub {
1814             # Optionally use other realm than current logged in user:
1815             # my $modify = modify_group( { group_name => 'who cares',
1816             # description => 'tired of writing' },
1817             # { id => '1', realm => 'admins' } );
1818             #
1819             # description is optional
1820             my $modify = modify_group( { group_name => 'who cares',
1821             description => 'tired of writing' },
1822             { id => '1' } );
1823             # This is annoying backwards when compared to
1824             # Dancer::Plugin::Database, sorry about that.
1825             # One could argue this isn't necessary.
1826            
1827             if ( $modify ) {
1828             # success
1829             }
1830              
1831             #.......
1832             };
1833              
1834             Keyword 'modify_group' modifies an existing group.
1835             One can also use a different realm by adding 'realm'.
1836             One can also optionally give the group a description.
1837             If you wish to modify the group name then you'll need to
1838             provide the group id else you just provide the group name or id.
1839             If the group was modified returns true otherwise reutrns false.
1840              
1841             =item modify_permission - Modify existing permission
1842              
1843             get '/modify/permission' => sub {
1844             # Optionally use other realm than current logged in user:
1845             # my $modify = modify_permission( { permission_name => 'write',
1846             # description => 'meh' },
1847             # { id => '1', realm => 'admins' } );
1848             #
1849             # description is optional
1850             my $modify = modify_permission( { permission_name => 'write',
1851             description => 'meh' },
1852             { id => '1' } );
1853             # This is annoying backwards when compared to
1854             # Dancer::Plugin::Database, sorry about that.
1855             # One could argue this isn't necessary.
1856            
1857             if ( $modify ) {
1858             # success
1859             }
1860              
1861             #.......
1862             };
1863              
1864             Keyword 'modify_permission' modifies an existing permission.
1865             One can also use a different realm by adding 'realm'.
1866             One can also optionally give the group a description.
1867             If you wish to modify the permission name then you'll need to
1868             provide the permission id else you just provide the permission name or id.
1869             If the permission was modified returns true otherwise reutrns false.
1870              
1871             =back
1872              
1873             =head1 DELETEING USERS, ROLES, GROUPS & PERMISSIONS
1874              
1875             =over
1876              
1877             =item delete_user - Deletes user
1878              
1879             get '/delete/user' => sub {
1880             # Optionally use other realm than current logged in user:
1881             # my $delete = delete_user( { username => 'robin' },
1882             # { realm => 'admins' } );
1883              
1884             my $delete = delete_user( { username => 'robin' } );
1885            
1886             if ( $delete ) {
1887             # success
1888             }
1889            
1890             #.......
1891             };
1892              
1893             Keyword 'delete_user' deletes the user specified by username.
1894             One can also use a different realm by adding 'realm'.
1895             If the user was deleted returns true otherwise returns false.
1896              
1897             =item delete_role - Deletes role
1898              
1899             get '/delete/role' => sub {
1900             # Optionally use other realm than current logged in user:
1901             # my $delete = delete_role( { role_name => 'some role' },
1902             # { realm => 'admins' } );
1903              
1904             my $delete = delete_role( { role_name => 'some role' } );
1905            
1906             if ( $delete ) {
1907             # success
1908             }
1909            
1910             #.......
1911             };
1912              
1913             Keyword 'delete_role' deletes the role specified by role name.
1914             One can also use a different realm by adding 'realm'.
1915             If the role was deleted returns true otherwise returns false.
1916              
1917             =item delete_group - Deletes group
1918              
1919             get '/delete/group' => sub {
1920             # Optionally use other realm than current logged in user:
1921             # my $delete = delete_group( { group_name => 'some group' },
1922             # { realm => 'admins' } );
1923              
1924             my $delete = delete_group( { group_name => 'some group' } );
1925            
1926             if ( $delete ) {
1927             # success
1928             }
1929            
1930             #.......
1931             };
1932              
1933             Keyword 'delete_group' deletes the group specified by group name.
1934             One can also use a different realm by adding 'realm'.
1935             If the group was deleted returns true otherwise returns false.
1936              
1937             =item delete_permission - Deletes permission
1938              
1939             get '/delete/permission' => sub {
1940             # Optionally use other realm than current logged in user:
1941             # my $delete = delete_permission( { permission_name => 'some perm' },
1942             # { realm => 'admins' } );
1943              
1944             my $delete = delete_permission( { permission_name => 'some perm' } );
1945            
1946             if ( $delete ) {
1947             # success
1948             }
1949            
1950             #.......
1951             };
1952              
1953             Keyword 'delete_permission' deletes the permission
1954             specified by permission name.
1955             One can also use a different realm by adding 'realm'.
1956             If the group was deleted returns true otherwise returns false.
1957              
1958             =back
1959              
1960             =head1 PASSWORDS AND HASHING
1961              
1962             =over
1963              
1964             =item generate_hash - Turn clear text into hash
1965              
1966             get '/generate_hash' => sub {
1967             # Optionally use other realm than current logged in user:
1968             # my $hash = generate_hash( { password => params->{password} }, { realm => 'test' } );
1969            
1970             my $hash = generate_hash( { password => params->{password} } );
1971            
1972             #.......
1973             };
1974              
1975             Keyword 'generate_hash' will turn your clear text password into the
1976             a SHA2 512bit hash with salt using L<Crypt::PBKDF2>.
1977             Which is the default hashing method employed with YARBAC.
1978             However, when creating or modify a user with YARBAC
1979             you don't need to call on this as hashing happens automatically.
1980              
1981             =item password_strength - Checks a clear text password for its strength
1982              
1983             post '/password_strength' => sub {
1984             # Optionally use other realm than current logged in user:
1985             my $strength = password_strength( { password => params->{password} }, { realm => 'admin' } );
1986            
1987             my $strength = password_strength( { password => params->{password} } );
1988            
1989             # returns a hashref like so:
1990             # { score => $score, error => $error, errors => \@errors }
1991             # Depending on what is enabled in your config,
1992             # possile error codes are in error arrayref are:
1993             #{ code => 1, message => 'Password is empty' }
1994             #{ code => 2, message => 'Password is too short' }
1995             #{ code => 3, message => 'Password is too long' }
1996             #{ code => 4, message => 'Password must contain special characters' }
1997             #{ code => 5, message => 'Password must contain control characters' }
1998             #{ code => 6, message => 'Password must not be repeating characters' }
1999             #{ code => 7, message => 'Password must contain at least one uppercase character' }
2000             #{ code => 8, message => 'Password must contain at least one lowercase character' }
2001             #{ code => 9, message => 'Password must contain at least one number character' }
2002             #{ code => 10, message => 'Password scored x points, must score at least y points' }
2003              
2004             unless ( $strength->{error} ) {
2005             # Looks like the password strength isn't strong enough.
2006             }
2007            
2008             #.......
2009             };
2010              
2011             Keyword 'password_strength' will check a clear text password for its strength.
2012             This is not enforced when creating or updating a user via YARBAC
2013             thus is completely optional.
2014             If errors are found returns a hashref with 'error' as true otherwise 'error'
2015             is false.
2016              
2017             =back
2018              
2019             =head1 MISCELLANEOUS
2020              
2021             =over
2022              
2023             =item provider - Returns the backend provider object
2024              
2025             get '/provider' => sub {
2026             my $provider = provider();
2027             #.......
2028             };
2029              
2030             Keyword 'provider' returns the backend provider object.
2031             Wouldn't recommend it without a good reason.
2032              
2033             =back
2034              
2035             =head1 AUTHOR
2036              
2037             Sarah Fuller <sarah@averna.id.au>
2038              
2039             =head1 COPYRIGHT AND LICENSE
2040              
2041             This software is copyright (c) 2015 by Sarah Fuller.
2042              
2043             This is free software; you can redistribute it and/or modify it under
2044             the same terms as the Perl 5 programming language system itself.
2045              
2046             =cut
2047              
2048             __END__
2049              
2050             # ABSTRACT: Yet Another Role Based Access Control Framework
2051