line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Tree::Authz; |
2
|
1
|
|
|
1
|
|
21946
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
3
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
4
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
101
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# persistence doesn't work - propagating changes to other processes |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# TODO - plugin sets - specify a search path e.g. My::App::Roles |
9
|
|
|
|
|
|
|
# any module My::App::Roles::rolename for a rolename defined in the authz |
10
|
|
|
|
|
|
|
# is automatically loaded into that role |
11
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
364
|
use Lingua::EN::Inflect::Number (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Symbol; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use Tree::Authz::Role; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use base 'Class::Data::Inheritable'; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( '_AllRoles' ); |
20
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( '_database' ); |
21
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( '__namespace' ); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
__PACKAGE__->_AllRoles( {} ); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $VERSION = '0.02_2'; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 NAME |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Tree::Authz - inheritance-based authorization scheme |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 VERSION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
0.02_1 |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DEVELOPER RELEASE |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Re-organised to return objects (blessed into the new class C), |
38
|
|
|
|
|
|
|
instead of strings, which are now referred to as C rather than C |
39
|
|
|
|
|
|
|
in the documentation. Some method names changed to reflect the terminology. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 SYNOPSIS |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
use Tree::Authz; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $roles = { superuser => [ qw( spymasters politicians ) ], |
46
|
|
|
|
|
|
|
spymasters => [ qw( spies moles ) ], |
47
|
|
|
|
|
|
|
spies => [ 'informants' ], |
48
|
|
|
|
|
|
|
informants => [ 'base' ], |
49
|
|
|
|
|
|
|
moles => [ 'base' ], |
50
|
|
|
|
|
|
|
politicians => [ 'citizens' ], |
51
|
|
|
|
|
|
|
citizens => [ 'base' ], |
52
|
|
|
|
|
|
|
}; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my $authz = Tree::Authz->setup_hierarchy( $roles, 'SpyLand' ); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $superuser = $authz->role( 'superuser' ); |
57
|
|
|
|
|
|
|
my $spies = $authz->role( 'spies' ); |
58
|
|
|
|
|
|
|
my $citizens = $authz->role( 'citizens' ); |
59
|
|
|
|
|
|
|
my $base = $authz->role( 'base' ); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
$spies ->setup_permissions( [ qw( read_secrets wear_disguise ) ] ); |
62
|
|
|
|
|
|
|
$citizens->setup_permissions( 'vote' ); |
63
|
|
|
|
|
|
|
$base ->setup_permissions( 'breathe' ); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
foreach my $role ( $superuser, $spies, $citizens, $base ) { |
66
|
|
|
|
|
|
|
foreach my $ability ( qw( unspecified_ability |
67
|
|
|
|
|
|
|
spy |
68
|
|
|
|
|
|
|
spies |
69
|
|
|
|
|
|
|
read_secrets |
70
|
|
|
|
|
|
|
wear_disguise |
71
|
|
|
|
|
|
|
vote |
72
|
|
|
|
|
|
|
breathe |
73
|
|
|
|
|
|
|
can ) ) { |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
if ( $role->can( $ability ) ) { |
76
|
|
|
|
|
|
|
print "$role can '$ability'\n"; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
else { |
79
|
|
|
|
|
|
|
print "$role cannot '$ability'\n"; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# prints: |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
superuser can 'unspecified_ability' # superpowers! |
87
|
|
|
|
|
|
|
superuser can 'spy' |
88
|
|
|
|
|
|
|
superuser can 'spies' |
89
|
|
|
|
|
|
|
superuser can 'read_secrets' |
90
|
|
|
|
|
|
|
superuser can 'wear_disguise' |
91
|
|
|
|
|
|
|
superuser can 'vote' |
92
|
|
|
|
|
|
|
superuser can 'breathe' |
93
|
|
|
|
|
|
|
superuser can 'can' |
94
|
|
|
|
|
|
|
spies cannot 'unspecified_ability' |
95
|
|
|
|
|
|
|
spies can 'spy' |
96
|
|
|
|
|
|
|
spies can 'spies' |
97
|
|
|
|
|
|
|
spies can 'read_secrets' |
98
|
|
|
|
|
|
|
spies can 'wear_disguise' |
99
|
|
|
|
|
|
|
spies can 'vote' |
100
|
|
|
|
|
|
|
spies can 'breathe' |
101
|
|
|
|
|
|
|
spies can 'can' |
102
|
|
|
|
|
|
|
citizens cannot 'unspecified_ability' |
103
|
|
|
|
|
|
|
citizens cannot 'spy' |
104
|
|
|
|
|
|
|
citizens cannot 'spies' |
105
|
|
|
|
|
|
|
citizens cannot 'read_secrets' |
106
|
|
|
|
|
|
|
citizens cannot 'wear_disguise' |
107
|
|
|
|
|
|
|
citizens can 'vote' |
108
|
|
|
|
|
|
|
citizens can 'breathe' |
109
|
|
|
|
|
|
|
citizens can 'can' |
110
|
|
|
|
|
|
|
base cannot 'unspecified_ability' |
111
|
|
|
|
|
|
|
base cannot 'spy' |
112
|
|
|
|
|
|
|
base cannot 'spies' |
113
|
|
|
|
|
|
|
base cannot 'read_secrets' |
114
|
|
|
|
|
|
|
base cannot 'wear_disguise' |
115
|
|
|
|
|
|
|
base cannot 'vote' |
116
|
|
|
|
|
|
|
base cannot 'breathe' # ! |
117
|
|
|
|
|
|
|
base cannot 'can' # !! |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# storing code on the nodes (roles) of the tree |
120
|
|
|
|
|
|
|
$spies->setup_abilities( read_secret => $coderef ); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
print $spies->read_secret( '/path/to/secret/file' ); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
$spies->setup_plugins( 'My::Spies::Skills' ); |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
$spies->fly( $jet ); # My::Spies::Skills::fly |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head1 DESCRIPTION |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Class for inheritable, role-based permissions system (Role Based Access |
131
|
|
|
|
|
|
|
Control - RBAC). |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Custom methods can be placed on role objects. Authorization can be performed |
134
|
|
|
|
|
|
|
either by checking whether the role name matches the required name, or by |
135
|
|
|
|
|
|
|
testing (via C) whether the role can perform the method required. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Two role are specified by default. At the top, Is can do anything |
138
|
|
|
|
|
|
|
(C<< $superuser->can( $action ) >> always returns a coderef). At the bottom, the |
139
|
|
|
|
|
|
|
I role can do nothing (C<< $base->can( $action ) >> always returns undef). |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
All roles are automatically capable of authorizing actions named for the |
142
|
|
|
|
|
|
|
singular and plural of the role name. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 ROADMAP |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
I'm planning to implement some of the main features and terminology described |
147
|
|
|
|
|
|
|
in this document, which describes a standard for Role Based Access Control: |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
http://csrc.nist.gov/rbac/rbacSTD-ACM.pdf |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Thanks to Kingsley Kerce for providing the link. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 METHODS |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
This class is a static class - all methods are class methods. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Some methods return L subclass objects. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head2 Namespaces and class methods |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
This class is designed to work in environments where multiple applications |
162
|
|
|
|
|
|
|
run within the same process (i.e. websites under C). If the optional |
163
|
|
|
|
|
|
|
namespace parameter is supplied to C, the roles are isolated |
164
|
|
|
|
|
|
|
to the specified namespace. All methods should be called through the |
165
|
|
|
|
|
|
|
class name returned from C. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
If your program is not operating in such an environment (e.g. CGI scripts), |
168
|
|
|
|
|
|
|
then you can completely ignore this parameter, and call class methods either |
169
|
|
|
|
|
|
|
through C, or through the string returned from C |
170
|
|
|
|
|
|
|
(which, funnily enough, will be 'Tree::Authz'). |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=over 4 |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item role( $role_name ) |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
Factory method, returns a L subclass |
177
|
|
|
|
|
|
|
object. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Sets up two permitted actions on the group - the singular and plural of |
180
|
|
|
|
|
|
|
the group name. B
|
181
|
|
|
|
|
|
|
name in a near future release>. Opinions welcome. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item new( $role_name ) |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
DEPRECATED. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Use C instead. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=item get_group( $group_name ) |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
DEPRECATED. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Use C instead. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub role { |
198
|
|
|
|
|
|
|
my ($proto, $role) = @_; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
croak 'No role name' unless $role; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
unless ( $proto->role_exists( $role ) ) { |
203
|
|
|
|
|
|
|
carp( "Unknown role: $role - using 'base' instead" ); |
204
|
|
|
|
|
|
|
$role = 'base'; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
my $authz_class = ref( $proto ) || $proto; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
my $class = "${authz_class}::Role::$role"; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
return $class->new( $role, $authz_class ); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub new { |
215
|
|
|
|
|
|
|
carp "'new' is deprecated - use 'role' instead"; |
216
|
|
|
|
|
|
|
goto &role; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub get_group { |
220
|
|
|
|
|
|
|
carp "'get_group' is deprecated - use 'role' instead"; |
221
|
|
|
|
|
|
|
goto &new; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=item role_exists( $role_name ) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Returns true if the specified group exists B within the hierarchy. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=item group_exists( $group_name ) |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
DEPRECATED. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Use C instead. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=cut |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub role_exists { exists $_[0]->_AllRoles->{ $_[1] } } |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub group_exists { |
242
|
|
|
|
|
|
|
carp "'group_exists' is deprecated - use 'role_exists' instead"; |
243
|
|
|
|
|
|
|
goto &role_exists; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item subrole_exists( $subrole_name, [ $role_name ] ) |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
B. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Give me a nudge if this would be useful. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Returns true if the specified role exists anywhere in the hierarchy |
253
|
|
|
|
|
|
|
underneath the current or specified role. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=cut |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub subrole_exists { croak 'subrole_exists method not implemented yet - email me' } |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item list_roles() |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Returns an array or arrayref of all the role names in the hierarchy, sorted by |
262
|
|
|
|
|
|
|
name. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item list_groups() |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
DEPRECATED. |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Use C instead. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=cut |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub list_roles { |
273
|
|
|
|
|
|
|
my @roles = sort keys %{ $_[0]->_AllRoles }; |
274
|
|
|
|
|
|
|
wantarray ? @roles : [ @roles ]; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub list_groups { |
278
|
|
|
|
|
|
|
carp "'list_groups' is deprecated - use 'list_roles' instead"; |
279
|
|
|
|
|
|
|
goto &list_roles; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=item dump_hierarchy( [ $namespace ] ) |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Get a simple printout of the structure of your hierarchy. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
This method Cs L. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
If you find yourself parsing the output and using it somehow in your code, let |
290
|
|
|
|
|
|
|
me know, and I'll find a Better Way to provide the data. This method is just |
291
|
|
|
|
|
|
|
intended for quick and dirty printouts and could B. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub dump_hierarchy { |
296
|
|
|
|
|
|
|
my ($proto) = @_; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $class = ref( $proto ) || $proto; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
require Devel::Symdump; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
my @classes = split( "\n", Devel::Symdump->isa_tree ); |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
my @wanted; |
305
|
|
|
|
|
|
|
my $start = 0; |
306
|
|
|
|
|
|
|
my $end = 0; |
307
|
|
|
|
|
|
|
my $supers = "${class}::Role::superuser"; |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
foreach my $possible ( @classes ) { |
310
|
|
|
|
|
|
|
$start = 1 if $possible =~ /^$supers/; |
311
|
|
|
|
|
|
|
if ( $start && $possible !~ /^$supers/ ) { |
312
|
|
|
|
|
|
|
$end = 1 if $possible =~ /^\w/; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
push( @wanted, $possible ) if ( $start && ! $end && $possible =~ __PACKAGE__ ); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
return join( "\n", @wanted ); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item setup_hierarchy( $groups, [ $namespace ] ) |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Class method. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
I<$groups> has: |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
keys - group names |
327
|
|
|
|
|
|
|
values - arrayrefs of subgroup name(s) |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Sets up a hierarchy of Perl classes representing the group structure. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
The hierarchy will be contained within the I<$namespace> top level if supplied. |
332
|
|
|
|
|
|
|
This makes it easy to set up several independent hierarchies to use within the |
333
|
|
|
|
|
|
|
same process, e.g. for different websites under C. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Returns a class name through which group objects can be retrieved and other |
336
|
|
|
|
|
|
|
class methods called. This will be 'Tree::Authz' if no namespace is specified. |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
If called with a I<$namespace> argument, then all loaded packages within the |
339
|
|
|
|
|
|
|
C<$namespace::Tree::Authz> symbol table hierarchy are removed (using |
340
|
|
|
|
|
|
|
L from the symbol |
341
|
|
|
|
|
|
|
table. This is experimental and may lead to bugs, the jury is still out. The |
342
|
|
|
|
|
|
|
purpose of this is to allow re-initialisation of the setup within a long-running |
343
|
|
|
|
|
|
|
process such as C. It could also allow dynamic updates to the |
344
|
|
|
|
|
|
|
hierarchy. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub setup_hierarchy { |
349
|
|
|
|
|
|
|
my ($proto, $roles_data, $namespace) = @_; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
croak( 'No roles data' ) unless $roles_data; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
my $class = ref( $proto ) || $proto; |
354
|
|
|
|
|
|
|
$class = "${namespace}::$class" if $namespace; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# If we are reloading, remove any existing hierarchy from the symbol table. |
357
|
|
|
|
|
|
|
# But not if there's no namespace, because then we would lose Tree::Authz |
358
|
|
|
|
|
|
|
# itself |
359
|
|
|
|
|
|
|
# Symbol::delete_package( $class ) if $namespace; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
my $roles_class = 'Tree::Authz::Role'; |
362
|
|
|
|
|
|
|
$roles_class = "${namespace}::$roles_class" if $namespace; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
my %roles; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
foreach my $role ( keys %$roles_data ) { |
367
|
|
|
|
|
|
|
my @isa = map { "${roles_class}::$_" } @{ $roles_data->{ $role } }; |
368
|
|
|
|
|
|
|
my $role_class = "${roles_class}::${role}"; |
369
|
|
|
|
|
|
|
$roles{ $role } = $role_class; |
370
|
|
|
|
|
|
|
no strict 'refs'; |
371
|
|
|
|
|
|
|
@{"${role_class}::ISA"} = @isa; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
my $supers_class = "${roles_class}::superuser"; |
375
|
|
|
|
|
|
|
my $base_class = "${roles_class}::base"; |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
{ |
378
|
|
|
|
|
|
|
no strict 'refs'; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# base for authz class |
381
|
|
|
|
|
|
|
# push( @{"${class}::ISA"}, 'Tree::Authz' ) if $namespace; |
382
|
|
|
|
|
|
|
# set, rather than push onto, because this has to be repeatably callable |
383
|
|
|
|
|
|
|
# to allow updates after editing |
384
|
|
|
|
|
|
|
@{"${class}::ISA"} = ( 'Tree::Authz' ) if $namespace; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# add a base group |
387
|
|
|
|
|
|
|
# push( @{"${base_class}::ISA"}, 'Tree::Authz::Role' ); # $roles_class ); |
388
|
|
|
|
|
|
|
@{"${base_class}::ISA"} = ( 'Tree::Authz::Role' ); |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# superuser always returns a subref from 'can', even if the specified |
391
|
|
|
|
|
|
|
# method doesn't exist. |
392
|
|
|
|
|
|
|
*{"${supers_class}::can"} = |
393
|
|
|
|
|
|
|
sub { UNIVERSAL::can( $_[0], $_[1] ) || sub {} }; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# base group cannot do anything |
396
|
|
|
|
|
|
|
*{"${base_class}::can"} = sub { |
397
|
|
|
|
|
|
|
my ($proto, @args) = @_; |
398
|
|
|
|
|
|
|
my $class = ref( $proto ) || $proto; |
399
|
|
|
|
|
|
|
return if $class =~ /::base$/; |
400
|
|
|
|
|
|
|
return UNIVERSAL::can( $proto, @args ); |
401
|
|
|
|
|
|
|
}; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# classdata methods have to come down here, after @ISA is set up for $class |
405
|
|
|
|
|
|
|
$class->_AllRoles( {} ); |
406
|
|
|
|
|
|
|
$class->_AllRoles->{ $_ } = $roles{ $_ } for keys %roles; |
407
|
|
|
|
|
|
|
$class->_AllRoles->{ superuser } = $supers_class; |
408
|
|
|
|
|
|
|
$class->_AllRoles->{ base } = $base_class; |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# __reload needs this |
411
|
|
|
|
|
|
|
$class->__namespace( $namespace ); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
foreach my $role ( keys %roles ) { |
414
|
|
|
|
|
|
|
my @cando = ( Lingua::EN::Inflect::Number::to_PL( $role ), |
415
|
|
|
|
|
|
|
Lingua::EN::Inflect::Number::to_S( $role ), |
416
|
|
|
|
|
|
|
); |
417
|
|
|
|
|
|
|
$class->setup_permissions_on_role( $role, \@cando ) |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
return $class; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=back |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head2 Persistence |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
L can be used independently of a persistence mechanism |
428
|
|
|
|
|
|
|
I C. However, if you want to manipulate the hierarchy at |
429
|
|
|
|
|
|
|
runtime, a persistence mechanism is required. The implementation is left up to |
430
|
|
|
|
|
|
|
you, but the API is defined. The persistence API should be |
431
|
|
|
|
|
|
|
implemented by the object passed to C. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=over |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=item setup_from_database( $database, [ $namespace ] ) |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
I<$database> should be an object that responds to the persistence API defined |
438
|
|
|
|
|
|
|
below. The object is stored as class data and is available via the C<_database> |
439
|
|
|
|
|
|
|
method. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=back |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head3 Pass-through methods |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
The following methods are passed on to the database object, after checking |
446
|
|
|
|
|
|
|
whether any changes would result in a recursive inheritance pattern, in which |
447
|
|
|
|
|
|
|
case they return false. The database methods should return true on success. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=over |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=item get_roles_data() |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
Returns a hashref. Keys are role names, values are arrayrefs of subroles. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
C calls this method on the database object, then passes |
456
|
|
|
|
|
|
|
the data on to C. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=item add_role( $new_role, $parent, [ $children ] ) |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Adds a new role to the scheme. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
I<$parent> is required, so no new top-level |
463
|
|
|
|
|
|
|
roles can be inserted. It's up to you to decide whether to raise an error or |
464
|
|
|
|
|
|
|
just return if I<$parent> is omitted. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
I<$children> can be a role name or an arrayref of role names. Defaults to |
467
|
|
|
|
|
|
|
C<'base'> if omitted. It might be worth checking if these roles already exist. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
At the moment I am assuming no multiple inheritance, but things are shaping up |
470
|
|
|
|
|
|
|
to look like there's no great difficulty about allowing it. If allowed, this |
471
|
|
|
|
|
|
|
method should check if I<$new_role> already exists. If it does, ignore any |
472
|
|
|
|
|
|
|
I<$children> (probably raise a warning), add <$new_role> to the sub-roles list |
473
|
|
|
|
|
|
|
of I<$parent>, and return without trying to insert I<$new_role> into the |
474
|
|
|
|
|
|
|
database (because it already exists). |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=item remove_role( $role ) |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Removes the role from the database, including finding and removing any |
479
|
|
|
|
|
|
|
occurrences of I<$role> in the sub-role lists of other roles. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Returns the list of subroles for the role that was removed, in case you want |
482
|
|
|
|
|
|
|
to put them somewhere else. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=item move_role( $role, $to ) |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Makes I<$role> a sub-role of I<$to>, and deletes it from the sub-roles list of |
487
|
|
|
|
|
|
|
its current parent. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=item add_subrole( $role, $subrole ) |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Adds a subrole to a role. Must remove C<'base'> from the subroles list if |
492
|
|
|
|
|
|
|
present. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=item remove_subrole( $role, $subrole ) |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Removes a subrole from a role. If the resulting list of subroles would be empty, |
497
|
|
|
|
|
|
|
must insert C<'base'>. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=cut |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub setup_from_database { |
502
|
|
|
|
|
|
|
my ($proto, $database, $namespace) = @_; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
croak( 'No database' ) unless $database; |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
my $authz = $proto->setup_hierarchy( $database->get_roles_data, $namespace ); |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# store away as class data |
509
|
|
|
|
|
|
|
$authz->_database( $database ); |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
return $authz; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# these methods all return true on success |
515
|
|
|
|
|
|
|
sub get_roles_data { shift->_database->get_roles_data( @_ ) } |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
sub remove_role { |
518
|
|
|
|
|
|
|
my ($proto) = @_; |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
$proto->_database->remove_role( @_ ); |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
$proto->__reload; |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub remove_subrole { |
526
|
|
|
|
|
|
|
my ($proto) = @_; |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
$proto->_database->remove_subrole( @_ ); |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
$proto->__reload; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# These methods look for potential recursion and return false if they find it. |
534
|
|
|
|
|
|
|
# If the potential child/subrole can/isa parent/role, then they can not be |
535
|
|
|
|
|
|
|
# put into the parent/child relationship specified, and the operations must |
536
|
|
|
|
|
|
|
# abort. |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# If the operation is OK, it proceeds and returns a true value on success. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub move_role { |
541
|
|
|
|
|
|
|
my ($proto, $role, $to) = @_; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
croak( 'No destination role in move_role' ) unless $to; |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
my @parents; |
546
|
|
|
|
|
|
|
foreach my $rl ( $proto->list_roles ) { |
547
|
|
|
|
|
|
|
my %subrls = map { $_ => 1 } $proto->role( $rl )->list_roles; |
548
|
|
|
|
|
|
|
push( @parents, $rl ) if $subrls{ $role }; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
unless ( @parents ) { |
552
|
|
|
|
|
|
|
croak( "Couldn't find parent(s) of $role" ); |
553
|
|
|
|
|
|
|
return; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
my $to_role = $proto->role( $to ); |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
foreach my $p ( @parents ) { |
559
|
|
|
|
|
|
|
return if $to_role->can( $p ); |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
# OK, let's do it |
563
|
|
|
|
|
|
|
$proto->_database->move_role( $role, $to ); |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
$proto->__reload; |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# $new_role wants to join $children as subrole of $parent |
569
|
|
|
|
|
|
|
sub add_role { |
570
|
|
|
|
|
|
|
my ($proto, $new_role, $parent, $children) = @_; |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
$children ||= 'base'; |
573
|
|
|
|
|
|
|
my @children = ref( $children ) ? @$children : ( $children ); |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# children must exist |
576
|
|
|
|
|
|
|
my %all_roles = map { $_ => 1 } $proto->list_roles; |
577
|
|
|
|
|
|
|
foreach my $child ( @children ) { |
578
|
|
|
|
|
|
|
return unless $all_roles{ $child }; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
# and none CAN parent |
582
|
|
|
|
|
|
|
foreach my $child ( @children ) { |
583
|
|
|
|
|
|
|
return if $proto->role( $child )->can( $parent ); |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
# OK, let's do it |
587
|
|
|
|
|
|
|
$proto->_database->add_role( $new_role, $parent, [ @children ] ); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
$proto->__reload; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
sub add_subrole { |
593
|
|
|
|
|
|
|
my ($proto, $role, $subrole) = @_; |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
return if $proto->role( $subrole )->can( $role ); |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# OK, let's do it |
598
|
|
|
|
|
|
|
$proto->_database->add_subrole ( $role, $subrole ); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
$proto->__reload; |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# attempt to load any changes back into the symbol table |
604
|
|
|
|
|
|
|
sub __reload { |
605
|
|
|
|
|
|
|
my ($proto) = @_; |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# delete_package will delete these |
608
|
|
|
|
|
|
|
my $namespace = $proto->__namespace; |
609
|
|
|
|
|
|
|
my $database = $proto->_database; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# Remove the current hierarchy from the symbol table. But not if there's |
612
|
|
|
|
|
|
|
# no namespace, because then we would lose Tree::Authz itself |
613
|
|
|
|
|
|
|
Symbol::delete_package( ref( $proto ) || $proto ) if $namespace; |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# $proto has namespace already in its name, but has been removed from |
616
|
|
|
|
|
|
|
# the symbol table, so have to use __PACKAGE__, which breaks |
617
|
|
|
|
|
|
|
# subclassability |
618
|
|
|
|
|
|
|
__PACKAGE__->setup_from_database( $database, $namespace ); |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=back |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head2 Adding authorizations |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=over |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=item setup_permissions_on_role( $role_name, $cando ) |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Class method version of C. |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=item setup_permissions_on_group( $group_name, $cando ) |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
DEPRECATED. |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
Use C instead. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=cut |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
sub setup_permissions_on_role { |
640
|
|
|
|
|
|
|
my ($class, $role, $cando) = @_; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
croak( 'Parameter(s) missing' ) unless $cando; |
643
|
|
|
|
|
|
|
croak( 'Not an instance method' ) if ref( $class ); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
my $role_class = "${class}::Role::$role"; |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
$role_class->_setup_perms( $cando ); |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
sub setup_permissions_on_group { |
651
|
|
|
|
|
|
|
carp "'setup_permissions_on_group' is deprecated - use 'setup_permissions_on_role' instead"; |
652
|
|
|
|
|
|
|
goto &setup_permissions_on_role; |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=item setup_abilities_on_role( $role_name, %code ) |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Class method version of C. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item setup_abilities_on_group( $group_name, %code ) |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
DEPRECATED. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
Use C instead. |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=cut |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
sub setup_abilities_on_role { |
668
|
|
|
|
|
|
|
my ($class, $role, %code) = @_; |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
croak( 'Not an instance method' ) if ref( $class ); |
671
|
|
|
|
|
|
|
croak( 'Nothing to set up' ) unless %code; |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
my $group_class = "${class}::Role::$role"; |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
$group_class->_setup_abil( %code ); |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
sub setup_abilities_on_group { |
679
|
|
|
|
|
|
|
carp "'setup_abilities_on_group' is deprecated - use 'setup_abilities_on_role' instead"; |
680
|
|
|
|
|
|
|
goto &setup_abilities_on_role; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=item setup_plugins_on_role( $role_name, $plugins ) |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
Class method version of C. |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=item setup_plugins_on_group( $group_name, $plugins ) |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Deprecated version of C. |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=cut |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
sub setup_plugins_on_role { |
694
|
|
|
|
|
|
|
my ($class, $role, $plugins) = @_; |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
croak( 'Parameter(s) missing' ) unless $plugins; |
697
|
|
|
|
|
|
|
croak( 'Not an instance method' ) if ref( $class ); |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
my $group_class = "${class}::Role::$role"; |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
$group_class->_setup_plugins( $plugins ); |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
sub setup_plugins_on_group { |
705
|
|
|
|
|
|
|
carp "'setup_plugins_on_group' is deprecated - use 'setup_plugins_on_role' instead"; |
706
|
|
|
|
|
|
|
goto &setup_plugins_on_role; |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=back |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=cut |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
1; |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=head1 CHANGES |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
The deprecation policy is: |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
1) DEPRECATED methods issue a warning (via C) and then call the new |
721
|
|
|
|
|
|
|
method. They will be documented next to the replacement method. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
2) OBSOLETE methods will croak. These will be documented in a separate section. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
3) Removed methods will be documented in a separate section, in the first |
726
|
|
|
|
|
|
|
version they no longer exist in. |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Main changes in 0.02 |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
- changed terminology to refer to I instead of I. Deprecated |
731
|
|
|
|
|
|
|
all methods with I in their name. These methods now issue a |
732
|
|
|
|
|
|
|
warning via C, and will be removed in a future release. |
733
|
|
|
|
|
|
|
- added a new class to represent a role - L. |
734
|
|
|
|
|
|
|
L is now a static class (all its methods are |
735
|
|
|
|
|
|
|
class methods). The objects it returns from some methods are subclasses |
736
|
|
|
|
|
|
|
of L. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head1 TODO |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Roles are now represented by their own class. This should make it easier to |
741
|
|
|
|
|
|
|
add constraints and other RBAC features. |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
More methods for returning meta information, e.g. immediate subroles of a |
744
|
|
|
|
|
|
|
role, all subroles of a role, list available actions of a role and its |
745
|
|
|
|
|
|
|
subroles. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
Might be nice to register users with roles. |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Make role objects be singletons - not necessary if the only data they carry is |
750
|
|
|
|
|
|
|
their own name. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
Under C, all setup of hierarchies and permissions must be completed |
753
|
|
|
|
|
|
|
during server startup, before the startup process forks off Apache children. |
754
|
|
|
|
|
|
|
It would be nice to have some way of communicating updates to other processes. |
755
|
|
|
|
|
|
|
Alternatively, you could run the full startup sequence every time you need to |
756
|
|
|
|
|
|
|
access a Tree::Authz role, but that seems sub-optimal. |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
L, |
761
|
|
|
|
|
|
|
L. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
Optional - L. |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
L for the test suite. |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
=head1 BUGS |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
Please report all bugs via the CPAN Request Tracker at |
770
|
|
|
|
|
|
|
L. |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
Copyright 2004 by David Baird. |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
777
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=head1 AUTHOR |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
David Baird, C |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=head1 SEE ALSO |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
L, L. |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
=cut |