File Coverage

blib/lib/Dancer2/Plugin/HTTP/Auth/Extensible.pm
Criterion Covered Total %
statement 162 192 84.3
branch 70 98 71.4
condition 11 25 44.0
subroutine 25 27 92.5
pod 8 15 53.3
total 276 357 77.3


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::HTTP::Auth::Extensible;
2              
3 5     5   1808667 use warnings;
  5         13  
  5         145  
4 5     5   29 use strict;
  5         10  
  5         100  
5              
6 5     5   26 use Carp;
  5         11  
  5         317  
7 5     5   3932 use Dancer2::Plugin;
  5         175291  
  5         33  
8 5     5   8728 use Class::Load qw(try_load_class);
  5         10  
  5         260  
9              
10 5     5   4491 use HTTP::Headers::ActionPack::Authorization;
  5         181357  
  5         167  
11 5     5   3942 use HTTP::Headers::ActionPack::WWWAuthenticate;
  5         902  
  5         13867  
12              
13             our $VERSION = '0.121';
14              
15              
16             =head1 NAME
17              
18             Dancer2::Plugin::HTTP::Auth::Extensible - extensible authentication framework for Dancer2 apps
19              
20             =head1 DESCRIPTION
21              
22             A user authentication and authorisation framework plugin for Dancer2 apps.
23              
24             Makes it easy to require a user to be logged in to access certain routes,
25             provides role-based access control, and supports various authentication
26             methods/sources (config file, database, Unix system users, etc).
27              
28             Designed to support multiple authentication realms and to be as extensible as
29             possible, and to make secure password handling easy (the base class for auth
30             providers makes handling C-style hashed passwords really simple, so you
31             have no excuse for storing plain-text passwords).
32              
33              
34             =head1 SYNOPSIS
35              
36             Configure the plugin to use the authentication provider class you wish to use:
37              
38             plugins:
39             HTTP::Auth::Extensible:
40             realms:
41             users:
42             provider: Example
43             ....
44              
45             The configuration you provide will depend on the authentication provider module
46             in use. For a simple example, see
47             L.
48              
49             Define that a user must be logged in and have the proper permissions to
50             access a route:
51              
52             get '/secret' => http_require_role Confidant => sub { tell_secrets(); };
53              
54             Define that a user must be logged in to access a route - and find out who is
55             logged in with the C keyword:
56              
57             get '/users' => http_require_authentication sub {
58             my $user = http_authenticated_user;
59             return "Hi there, $user->{username}";
60             };
61              
62             =head1 AUTHENTICATION PROVIDERS
63              
64             This framework builds on top of L. For a
65             full explenation of the providers check that manual.
66              
67             For flexibility, that authentication framework uses simple authentication
68             provider classes, which implement a simple interface and do whatever is required
69             to authenticate a user against the chosen source of authentication.
70              
71             For an example of how simple provider classes are, so you can build your own if
72             required or just try out this authentication framework plugin easily,
73             see L.
74              
75             That framework supplies the following providers out-of-the-box:
76              
77             =over 4
78              
79             =item L
80              
81             Authenticates users using system accounts on Linux/Unix type boxes
82              
83             =item L
84              
85             Authenticates users stored in a database table
86              
87             =item L
88              
89             Authenticates users stored in the app's config
90              
91             =back
92              
93             Need to write your own? Just subclass
94             L and implement the required
95             methods, and you're good to go!
96              
97             =head1 CONTROLLING ACCESS TO ROUTES
98              
99             Keywords are provided to check if a user is logged in / has appropriate roles.
100              
101             =over
102              
103             =item http_require_authentication - require the user to be authenticated
104              
105             get '/dashboard' => http_require_authentication sub { .... };
106              
107             If the user can not be authenticated, they will be recieve a HTTP response
108             status of C. Remember, it should actualy say 'Not
109             Authenticated'.
110              
111             Optionally, a realm name can be specified as an extra argument:
112              
113             get 'outer_space'
114             => http_require_authentication 'outer_space'
115             => sub { .... };
116              
117             =item http_require_role - require the user to have a specified role
118              
119             get '/beer' => http_require_role BeerDrinker => sub { ... };
120              
121             Requires that the user can be authenticated as a user who has the specified
122             role. If the user can not be authenticated, they will get a C<401 Unautorized>
123             response. If they are logged in, but do not have the required role, they will
124             recieve a C<403 Forbidden> response.
125              
126             =item http_require_any_roles - require the user to have one of a list of roles
127              
128             get '/drink' => require_any_role [qw(BeerDrinker VodaDrinker)] => sub {
129             ...
130             };
131              
132             Same as L except that a user has any one (or more) of the
133             roles listed.
134              
135             =item require_all_roles - require the user to have all roles listed
136              
137             get '/foo' => require_all_roles [qw(Foo Bar)] => sub { ... };
138              
139             Same as L except that a user has all of the roles listed.
140              
141             =back
142              
143             =head2 Replacing the Default C< 401 > and C< 403 > Pages
144              
145              
146             =head2 Keywords
147              
148             =over
149              
150             =item http_require_authentication
151              
152             Used to wrap a route which requires a user can be authenticated to access
153             it.
154              
155             get '/secret' => http_require_authentication sub { .... };
156             get '/secret' => http_require_authentication 'realm-name' sub { .... };
157              
158             =cut
159              
160             sub http_require_authentication {
161 9     9 1 285868 my $dsl = shift;
162 9 100       52 my $realm = (@_ == 2) ? shift : http_default_realm($dsl);
163 9         455 my $coderef = shift;
164              
165             return sub {
166 11 50 33 11   467370 if (!$coderef || ref $coderef ne 'CODE') {
167 0         0 warn "Invalid http_require_authentication usage, please see docs";
168             }
169            
170 11         50 my $user = http_authenticated_user($dsl, $realm);
171 10 100       155 if (!$user) {
172             # $dsl->execute_hook('http_authentication_required', $coderef);
173             # # TODO: see if any code executed by that hook set up a response
174 5         10 $dsl->header('WWW-Authenticate' =>
175 5         34 qq|@{[ http_default_scheme($dsl) ]} realm="$realm"|
176             );
177 5         2230 $dsl->status(401); # Unauthorized
178             return
179 5         1586 qq|Authentication required to access realm: |
180             . qq|'$realm'|;
181             }
182 5         28 return $coderef->($dsl);
183 9         74 };
184             }
185              
186             register http_require_authentication => \&http_require_authentication;
187             register http_requires_authentication => \&http_require_authentication;
188              
189             =item require_role
190              
191             Used to wrap a route which requires a user can be authenticated with the
192             specified role in order to access it.
193              
194             get '/beer' => require_role BeerDrinker => sub { ... };
195             get '/beer' => require_role BeerDrinker 'realm-name' => sub { ... };
196              
197             You can also provide a regular expression, if you need to match the role using a
198             regex - for example:
199              
200             get '/beer' => http_require_role qr/Drinker$/ => sub { ... };
201              
202             =cut
203              
204             sub http_require_role {
205 5     5 1 31 return _build_wrapper(@_, 'single');
206             }
207              
208             register http_require_role => \&http_require_role;
209             register http_requires_role => \&http_require_role;
210              
211             =item http_require_any_role
212              
213             Used to wrap a route which requires a user can be authenticated with any
214             one (or more) of the specified roles in order to access it.
215              
216             get '/foo' => http_require_any_role [qw(Foo Bar)] => sub { ... };
217             get '/foo' => http_require_any_role [qw(Foo Bar)] 'realm-name' => sub { ... };
218              
219             =cut
220              
221             sub http_require_any_role {
222 1     1 1 11 return _build_wrapper(@_, 'any');
223             }
224              
225             register http_require_any_role => \&http_require_any_role;
226             register http_requires_any_role => \&http_require_any_role;
227              
228             =item http_require_all_roles
229              
230             Used to wrap a route which requires a user can be authenticated with all
231             of the roles listed in order to access it.
232              
233             get '/foo' => http_require_all_roles [qw(Foo Bar)] => sub { ... };
234             get '/foo' => http_require_all_roles [qw(Foo Bar)] 'realm-name' => sub { ... };
235              
236             =cut
237              
238             sub http_require_all_roles {
239 2     2 1 16 return _build_wrapper(@_, 'all');
240             }
241              
242             register http_require_all_roles => \&http_require_all_roles;
243             register http_requires_all_roles => \&http_require_all_roles;
244              
245              
246             sub _build_wrapper {
247 8     8   11 my $dsl = shift;
248 8         14 my $require_role = shift;
249 8 50       24 my $realm = (@_ == 3) ? shift : http_default_realm($dsl);
250 8         575 my $coderef = shift;
251 8         11 my $mode = shift;
252              
253             return sub {
254 8 50 33 8   70874 if (!$coderef || ref $coderef ne 'CODE') {
255 0         0 warn "Invalid http_require_authentication usage, please see docs";
256             }
257            
258 8         25 my $user = http_authenticated_user($dsl, $realm);
259 8 50       143 if (!$user) {
260             # $dsl->execute_hook('http_authentication_required', $coderef);
261             # # TODO: see if any code executed by that hook set up a response
262 0         0 $dsl->header('WWW-Authenticate' =>
263 0         0 qq|@{[ http_default_scheme($dsl) ]} realm="$realm"|
264             );
265 0         0 $dsl->status(401); # Unauthorized
266             return
267 0         0 qq|Authentication required to access realm: |
268             . qq|'$realm'|;
269             }
270            
271 8 100       34 my @role_list = ref $require_role eq 'ARRAY'
272             ? @$require_role
273             : $require_role;
274 8         9 my $role_match;
275 8 100       33 if ($mode eq 'single') {
    100          
    50          
276 5         14 for (user_roles($dsl)) {
277 10 100 100     24 $role_match++ and last if _smart_match($_, $require_role);
278             }
279             } elsif ($mode eq 'any') {
280 1         3 my %role_ok = map { $_ => 1 } @role_list;
  2         8  
281 1         4 for (user_roles($dsl)) {
282 2 100 50     12 $role_match++ and last if $role_ok{$_};
283             }
284             } elsif ($mode eq 'all') {
285 2         3 $role_match++;
286 2         5 for my $role (@role_list) {
287 4 100       11 if (!user_has_role($dsl, $role)) {
288 1         2 $role_match = 0;
289 1         2 last;
290             }
291             }
292             }
293 8 100       24 if (!$role_match) {
294              
295             # $dsl->execute_hook('http_permission_denied', $coderef);
296             # # TODO: see if any code executed by that hook set up a response
297 3         12 $dsl->status(403); # Forbidden
298             return
299 3         369 qq|Permission denied for resource: |
300 3         12 . qq|'@{[ $dsl->request->path ]}'|;
301             }
302            
303             # We're happy with their roles, so go head and execute the route
304             # handler coderef.
305 5         18 return $coderef->($dsl);
306              
307 8         71 }; # return sub
308             } # _build_wrapper
309              
310              
311              
312             =item authenticated_user
313              
314             Returns a hashref of details of the currently authenticated user, if there is one.
315              
316             The details you get back will depend upon the authentication provider in use.
317              
318             =cut
319              
320             sub http_authenticated_user {
321 19     19 0 44 my $dsl = shift;
322 19   33     123 my $realm = shift || http_default_realm($dsl);
323            
324 19 100       65 if ( http_authenticate_user($dsl, $realm) ) { # undef unless http_authenticate_user
325 13         44 my $provider = auth_provider($dsl, http_realm($dsl));
326 13         48 return $provider->get_user_details(
327             http_username($dsl),
328             http_realm($dsl)
329             );
330             } else {
331 5         11 return;
332             }
333             }
334             register http_authenticated_user => \&http_authenticated_user;
335              
336             =item user_has_role
337              
338             Check if a user has the role named.
339              
340             By default, the currently-logged-in user will be checked, so you need only name
341             the role you're looking for:
342              
343             if (user_has_role('BeerDrinker')) { pour_beer(); }
344              
345             You can also provide the username to check;
346              
347             if (user_has_role($user, $role)) { .... }
348              
349             =cut
350              
351             sub user_has_role {
352 4     4 1 5 my $dsl = shift;
353 4         87 my $session = $dsl->app->session;
354              
355 4         1074 my ($username, $want_role);
356 4 50       11 if (@_ == 2) {
357 0         0 ($username, $want_role) = @_;
358             } else {
359 4         9 $username = http_username($dsl);
360 4         25 $want_role = shift;
361             }
362              
363 4 50       12 return unless defined $username;
364              
365 4         11 my $roles = user_roles($dsl, $username);
366              
367 4         9 for my $has_role (@$roles) {
368 7 100       26 return 1 if $has_role eq $want_role;
369             }
370              
371 1         5 return 0;
372             }
373             register user_has_role => \&user_has_role;
374              
375             =item user_roles
376              
377             Returns a list of the roles of a user.
378              
379             By default, roles for the currently-logged-in user will be checked;
380             alternatively, you may supply a username to check.
381              
382             Returns a list or arrayref depending on context.
383              
384             =cut
385              
386             sub user_roles {
387 10     10 1 18 my ($dsl, $username, $realm) = @_;
388 10         202 my $session = $dsl->app->session;
389              
390 10 100       13072 $username = http_username($dsl) unless defined $username;
391              
392 10 50       53 my $search_realm = ($realm ? $realm : '');
393              
394 10         21 my $roles = auth_provider($dsl, $search_realm)->get_user_roles($username);
395 10 50       159 return unless defined $roles;
396 10 100       38 return wantarray ? @$roles : $roles;
397             }
398             register user_roles => \&user_roles;
399              
400              
401             =item authenticate_user
402              
403             Usually you'll want to let the built-in authentication handling code deal with
404             authenticating users, but in case you need to do it yourself, this keyword
405             accepts a username and password, and optionally a specific realm, and checks
406             whether the username and password are valid.
407              
408             For example:
409              
410             if (authenticate_user($username, $password)) {
411             ...
412             }
413              
414             If you are using multiple authentication realms, by default each realm will be
415             consulted in turn. If you only wish to check one of them (for instance, you're
416             authenticating an admin user, and there's only one realm which applies to them),
417             you can supply the realm as an optional third parameter.
418              
419             In boolean context, returns simply true or false; in list context, returns
420             C<($success, $realm)>.
421              
422             =cut
423              
424             sub http_authenticate_user {
425 19     19 0 32 my $dsl = shift;
426 19   33     54 my $realm = shift || http_default_realm($dsl);
427            
428 19         56 http_realm_exists($dsl, $realm);
429              
430 18 100       103 unless ($dsl->request->header('Authorization')) {
431 4 50       671 return wantarray ? (0, undef) : 0;
432             }
433             # my ($username, $password) = $dsl->request->headers->authorization_basic;
434            
435 14         2404 my $auth
436             = HTTP::Headers::ActionPack::Authorization::Basic
437             ->new_from_string($dsl->request->header('Authorization'));
438 14         1063 my $username = $auth->username;
439 14         229 my $password = $auth->password;
440            
441             # TODO For now it only does Basic authentication
442             # Once we have Digest and others, it needs to choose itself
443            
444 14 50       88 my @realms_to_check = $realm ? ($realm) : (keys %{ plugin_setting->{realms} });
  0         0  
445              
446 14         36 for my $realm (@realms_to_check) { # XXX we only should have 1 ????
447 14         78 $dsl->app->log ( debug => "Attempting to authenticate $username against realm $realm");
448 14         10394 my $provider = auth_provider($dsl, $realm);
449 14 100       95 if ($provider->authenticate_user($username, $password)) {
450 13         473 $dsl->app->log ( debug => "$realm accepted user $username");
451 13         5700 $dsl->vars->{'http_username'} = $username;
452             # don't do `http_username($dsl, $username)`, SECURITY BREACH
453 13         142 $dsl->vars->{'http_realm' } = $realm;
454             # don't do `http_username($dsl, $username)`, SECURITY BREACH
455 13 50       193 return wantarray ? ($username, $realm) : $username;
456             }
457             }
458              
459             # If we get to here, we failed to authenticate against any realm using the
460             # details provided.
461             # TODO: allow providers to raise an exception if something failed, and catch
462             # that and do something appropriate, rather than just treating it as a
463             # failed login.
464 1 50       42 return wantarray ? (0, undef) : 0;
465             }
466              
467             register http_authenticate_user => \&http_authenticate_user;
468              
469             sub http_default_realm {
470 29     29 0 54 my $dsl = shift;
471            
472 29 100       46 if (1 == keys %{ plugin_setting->{realms} }) {
  29         92  
473 25         2999 return (keys %{ plugin_setting->{realms} })[0]; # only the first key in scalar context
  25         65  
474             }
475 4 50       544 if (exists plugin_setting->{default_realm} ) {
476 4         497 return plugin_setting->{default_realm};
477             }
478              
479             die
480 0         0 qq|Internal Server Error: |
481             . qq|"multiple realms without default"|;
482            
483 0         0 return;
484            
485             } # http_default_realm
486              
487             #register http_default_realm => \&http_default_realm;
488              
489             sub http_realm_exists {
490 61     61 0 84 my $dsl = shift;
491 61   33     148 my $realm = shift || http_default_realm($dsl);
492              
493 61 100       90 unless (grep {$realm eq $_} keys %{ plugin_setting->{realms} }) {
  68         10300  
  61         183  
494 1         14 die
495             qq|Internal Server Error: |
496             . qq|"required realm does not exist: '$realm'"|;
497             }
498            
499 60         130 return $realm;
500            
501             } # http_realm_exists
502              
503             #register http_realm_exists => \&http_realm_exists;
504              
505              
506             sub http_default_scheme {
507 5     5 0 11 my $dsl = shift;
508 5   33     29 my $realm = shift || http_default_realm($dsl);
509              
510 5         778 http_realm_exists($dsl, $realm);
511            
512 5         9 my $scheme;
513            
514 5 50       15 if (exists plugin_setting->{realms}->{$realm}->{scheme} ) {
515 5         702 $scheme = plugin_setting->{realms}->{$realm}->{scheme};
516             }
517             else {
518 0         0 $scheme = "Basic";
519             }
520            
521 5         657 return $scheme;
522            
523             } # http_default_schema
524              
525             #register http_default_scheme => \&http_default_scheme;
526              
527              
528             sub http_scheme_known {
529 0     0 0 0 my $dsl = shift;
530 0         0 my $scheme = shift;
531              
532 0 0       0 unless (grep $scheme eq $_, ('Basic', 'Digest')) {
533 0         0 warn
534             qq|unknown scheme '$scheme'!|;
535 0         0 return;
536             }
537            
538 0         0 return $scheme;
539            
540             } # http_scheme_known
541              
542             #register http_scheme_known => \&http_scheme_known;
543              
544             =item http_username - gets or sets the name of the authenticated user
545              
546             WARNING: setting the username will issue a "SECURITY BREACH" warning. You
547             rarely want to impersonate another user.
548              
549             $my username = http_username;
550             http_username('new name');
551             http_username 'new name';
552              
553             If not inside an authenticated route (there is no authenticated user),
554             C< http_username > returns undef.
555              
556             =cut
557              
558             sub http_username {
559 29     29 1 42513 my $dsl = shift;
560            
561 29 100       103 unless ( exists $dsl->vars->{http_username} ) {
562 2         26 $dsl->app->log( warning =>
563             qq|'http_username' should only be used in an authenticated route|
564             );
565             }
566            
567 29 100       986 if (@_ == 1) { # CAUTION: use with care
568 2         8 $dsl->vars->{http_username} = shift;
569             my $message
570             = qq|POTENTIONAL SECURITY BREACH: |
571             . qq|"impersonating different user: '|
572             . $dsl->vars->{http_username}
573 2         18 . qq|'"|;
574 2         216 warn $message;
575 2         16 $dsl->app->log ( warning => $message );
576             }
577            
578 29 100       861 return unless exists $dsl->vars->{http_username};
579 28         222 return $dsl->vars->{http_username};
580            
581             } # http_username
582              
583             register http_username => \&http_username;
584              
585             =item http_realm - gets or sets the real of the current request
586              
587             WARNING: setting the realm will issue a "SECURITY BREACH" warning. You
588             rarely want to switch to another realm
589              
590             $my realm = http_realm;
591             http_realm('new name');
592             http_realm 'new name';
593              
594             If not inside an authenticated route (there is no authenticated user),
595             C< http_realm > returns undef.
596              
597             =cut
598              
599             sub http_realm {
600 32     32 1 40139 my $dsl = shift;
601            
602 32 100       97 unless ( exists $dsl->vars->{http_realm} ) {
603 2         28 $dsl->app->log( warning =>
604             qq|'http_realm' should only be used in an authenticated route|
605             );
606             }
607            
608 32 100       968 if (@_ == 1) { # CAUTION: use with care
609 2         8 $dsl->vars->{http_realm} = shift;
610             my $message
611             = qq|POTENTIONAL SECURITY BREACH: |
612             . qq|"switching to different realm: '|
613             . $dsl->vars->{http_realm}
614 2         16 . qq|'"|;
615 2         210 warn $message;
616 2         16 $dsl->app->log ( warning => $message );
617             }
618            
619 32 100       910 return unless exists $dsl->vars->{http_realm};
620 31         244 return $dsl->vars->{http_realm};
621            
622             } # http_realm
623              
624             register http_realm => \&http_realm;
625              
626             =back
627              
628             =head2 SAMPLE CONFIGURATION
629              
630             In your application's configuation file:
631              
632             plugins:
633             HTTP::Auth::Extensible:
634             # Set to 1 if you want to disable the use of roles (0 is default)
635             disable_roles: 0
636             # After /login: If no return_url is given: land here ('/' is default)
637             user_home_page: '/user'
638             # After /logout: If no return_url is given: land here (no default)
639             exit_page: '/'
640            
641             # List each authentication realm, with the provider to use and the
642             # provider-specific settings (see the documentation for the provider
643             # you wish to use)
644             realms:
645             realm_one:
646             provider: Database
647             db_connection_name: 'foo'
648            
649             default_realm: realm_xxx
650             # If there is more than one realm, is needed if no 'realm' is
651             # specified in http_requires_authentication.
652              
653             B that you B have a session provider configured. The
654             authentication framework B require sessions in order to track information about
655             the currently logged in user.
656              
657             =cut
658              
659             {
660             # Given a realm, returns a configured and ready to use instance of the provider
661             # specified by that realm's config.
662             my %realm_provider;
663             sub auth_provider {
664 37     37 0 120 my $dsl = shift;
665 37   66     116 my $realm = shift || http_default_realm($dsl);
666            
667 37         1468 http_realm_exists($dsl, $realm); # can be in void, it dies when false
668              
669             # First, if we already have a provider for this realm, go ahead and use it:
670 37 100       159 return $realm_provider{$realm} if exists $realm_provider{$realm};
671              
672             # OK, we need to find out what provider this realm uses, and get an instance
673             # of that provider, configured with the settings from the realm.
674 3 50       12 my $realm_settings = plugin_setting->{realms}->{$realm}
675             or die "Invalid realm $realm";
676             my $provider_class = $realm_settings->{provider}
677 3 50       457 or die "No provider configured - consult documentation for "
678             . "Dancer2::Plugin::Auth::Extensible";
679              
680 3 50       17 if ($provider_class !~ /::/) {
681 3         12 $provider_class = "Dancer2::Plugin::Auth::Extensible" . "::Provider::$provider_class";
682             }
683 3         16 my ($ok, $error) = try_load_class($provider_class);
684              
685 3 50       3311103 if (! $ok) {
686 0         0 die "Cannot load provider $provider_class: $error";
687             }
688              
689 3         64 return $realm_provider{$realm} = $provider_class->new($realm_settings);
690             }
691             }
692              
693             register_hook qw(http_authentication_required http_permission_denied);
694             register_plugin for_versions => [qw(1 2)];
695              
696              
697             # Given a class method name and a set of parameters, try calling that class
698             # method for each realm in turn, arranging for each to receive the configuration
699             # defined for that realm, until one returns a non-undef, then return the realm which
700             # succeeded and the response.
701             # Note: all provider class methods return a single value; if any need to return
702             # a list in future, this will need changing)
703             sub _try_realms {
704 0     0   0 my ($method, @args);
705 0         0 for my $realm (keys %{ plugin_setting->{realms} }) {
  0         0  
706 0         0 my $provider = auth_provider($realm);
707 0 0       0 if (!$provider->can($method)) {
708 0         0 die "Provider $provider does not provide a $method method!";
709             }
710 0 0       0 if (defined(my $result = $provider->$method(@args))) {
711 0         0 return $result;
712             }
713             }
714 0         0 return;
715             }
716              
717             on_plugin_import {
718             my $dsl = shift;
719             my $app = $dsl->app;
720              
721             };
722              
723              
724             # Replacement for much maligned and misunderstood smartmatch operator
725             sub _smart_match {
726 10     10   17 my ($got, $want) = @_;
727 10 100       24 if (!ref $want) {
    50          
    0          
728 8         36 return $got eq $want;
729             } elsif (ref $want eq 'Regexp') {
730 2         16 return $got =~ $want;
731             } elsif (ref $want eq 'ARRAY') {
732 0           return grep { $_ eq $got } @$want;
  0            
733             } else {
734 0           carp "Don't know how to match against a " . ref $want;
735             }
736             }
737              
738              
739              
740              
741             =head1 AUTHOR
742              
743             Theo van Hoesel, C<< >>
744              
745             HTTP Autneticate implementation based on:
746              
747             David Precious, C<< >>
748              
749             Dancer2 port of Dancer::Plugin::Auth::Extensible by:
750              
751             Stefan Hornburg (Racke), C<< >>
752              
753             =head1 BUGS / FEATURE REQUESTS
754              
755             This is an early version; there may still be bugs present or features missing.
756              
757             This is developed on GitHub - please feel free to raise issues or pull requests
758             against the repo at:
759             L
760              
761              
762              
763             =head1 ACKNOWLEDGEMENTS
764              
765             Valuable feedback on the early design of this module came from many people,
766             including Matt S Trout (mst), David Golden (xdg), Damien Krotkine (dams),
767             Daniel Perrett, and others.
768              
769             Configurable login/logout URLs added by Rene (hertell)
770              
771             Regex support for require_role by chenryn
772              
773             Support for user_roles looking in other realms by Colin Ewen (casao)
774              
775             LDAP provider added by Mark Meyer (ofosos)
776              
777             Config options for default login/logout handlers by Henk van Oers (hvoers)
778              
779             =head1 LICENSE AND COPYRIGHT
780              
781              
782             Copyright 2014 THEMA-MEDIA, Th.J. van Hoesel
783              
784             This program is free software; you can redistribute it and/or modify it
785             under the terms of either: the GNU General Public License as published
786             by the Free Software Foundation; or the Artistic License.
787              
788             See http://dev.perl.org/licenses/ for more information.
789              
790              
791             =cut
792              
793             1; # End of Dancer2::Plugin::HTTP::Auth::Extensible