File Coverage

blib/lib/Yancy/Plugin/Roles.pm
Criterion Covered Total %
statement 26 26 100.0
branch 6 6 100.0
condition 2 4 50.0
subroutine 6 6 100.0
pod 1 3 33.3
total 41 45 91.1


line stmt bran cond sub pod time code
1             package Yancy::Plugin::Roles;
2             our $VERSION = '1.088';
3             # ABSTRACT: Role-based access controls (RBAC)
4              
5             #pod =head1 SYNOPSIS
6             #pod
7             #pod plugin Yancy => ...;
8             #pod plugin Auth => ...;
9             #pod plugin Roles => {
10             #pod schema => 'roles',
11             #pod userid_field => 'username',
12             #pod };
13             #pod
14             #pod =head1 DESCRIPTION
15             #pod
16             #pod B This module is C and its API may change before
17             #pod Yancy v2.000 is released.
18             #pod
19             #pod This plugin provides user authorization based on roles. Roles are
20             #pod created by using the L method. User accounts are provided
21             #pod by L (or a subclass). Accounts are mapped to roles
22             #pod in the database.
23             #pod
24             #pod =head1 CONFIGURATION
25             #pod
26             #pod This plugin has the following configuration options.
27             #pod
28             #pod =head2 schema
29             #pod
30             #pod The name of the Yancy schema that holds role memberships. Required.
31             #pod
32             #pod =head2 userid_field
33             #pod
34             #pod The name of the field in the schema which is the user's identifier.
35             #pod This field should be named the same in both the user schema and the
36             #pod roles schema.
37             #pod
38             #pod =head2 role_field
39             #pod
40             #pod The name of the field in the schema which holds the role. Defaults to
41             #pod C.
42             #pod
43             #pod =head1 HELPERS
44             #pod
45             #pod This plugin has the following helpers.
46             #pod
47             #pod =head2 yancy.auth.has_role
48             #pod
49             #pod Return true if the current user has the given role.
50             #pod
51             #pod get '/admin' => sub {
52             #pod my $c = shift;
53             #pod return $c->reply->not_found unless $c->yancy->auth->has_role( 'admin' );
54             #pod };
55             #pod
56             #pod =head2 yancy.auth.require_role
57             #pod
58             #pod Validate there is a logged-in user and that the user belongs to the given
59             #pod role(s). Returns a callback that can be used in C.
60             #pod
61             #pod my $require_admin = $app->yancy->auth->require_role( 'admin' );
62             #pod my $admin_routes = $app->routes->under( '/admin', $require_admin );
63             #pod
64             #pod =head1 SEE ALSO
65             #pod
66             #pod L
67             #pod
68             #pod =cut
69              
70 2     2   1964 use Mojo::Base 'Mojolicious::Plugin';
  2         8  
  2         22  
71 2     2   519 use Yancy::Util qw( currym );
  2         7  
  2         1174  
72              
73             has schema =>;
74             has userid_field =>;
75             has role_field => 'role';
76              
77             sub register {
78 2     2 1 50 my ( $self, $app, $config ) = @_;
79             my $schema_name = $config->{schema}
80 2   50     11 || die "Error configuring Roles plugin: No schema defined\n";
81 2         8 $self->schema( $schema_name );
82              
83 2   50     143 my $schema = $app->yancy->schema( $schema_name )
84             || die sprintf(
85             q{Error configuring Roles plugin: Schema "%s" not found}."\n",
86             $schema_name,
87             );
88              
89 2         17 $self->userid_field( $config->{userid_field} );
90 2 100       19 $self->role_field( $config->{role_field} ) if $config->{role_field};
91 2         16 $app->helper(
92             'yancy.auth.require_role' => currym( $self, 'require_role' ),
93             );
94 2         6198 $app->helper(
95             'yancy.auth.has_role' => currym( $self, 'has_role' ),
96             );
97             }
98              
99             sub require_role {
100 1     1 0 5 my ( $self, $c, $role ) = @_;
101             return sub {
102 2     2   27503 my ( $c ) = @_;
103 2 100       15 return 1 if $self->has_role( $c, $role );
104             # XXX: Create `reply->unauthorized` helper
105 1         9 $c->stash(
106             template => 'yancy/auth/unauthorized',
107             status => 401,
108             );
109 1         36 $c->respond_to(
110             json => {},
111             html => {},
112             );
113 1         20755 return undef;
114             }
115 1         8 }
116              
117             sub has_role {
118 5     5 0 17 my ( $self, $c, $role ) = @_;
119 5 100       19 my $user = $c->yancy->auth->current_user or return undef;
120             my %search = (
121 3         15 $self->userid_field => $user->{ $self->userid_field },
122             $self->role_field => $role,
123             );
124 3         62 my ( $has_role ) = $c->yancy->list( $self->schema, \%search );
125 3         127 return !!$has_role;
126             }
127              
128             1;
129              
130             __END__