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