source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Acls.pm @ 984

Last change on this file since 984 was 918, checked in by nanardon, 12 years ago
  • avoid undef warning
File size: 6.5 KB
Line 
1package LATMOS::Accounts::Acls;
2
3use strict;
4use warnings;
5use LATMOS::Accounts::Log;
6
7our $VERSION = (q$Rev: 330 $ =~ /^Rev: (\d+) /)[0];
8
9sub new {
10    my ($class, $file) = @_;
11    my $acls = bless(
12        {
13            _acls => []
14        }, $class
15    );
16    if ($file) {
17        $acls->read_acl_file($file) or return;
18    }
19    $acls
20}
21
22=head1 ACL FILE FORMAT
23
24    OBJ_TYPE.ATTRIBUTE
25        USER:  read,write
26        USER2: read
27
28=over 4
29
30=item OBJ_TYPE is the type the object, '*' match all
31
32=item ATTRIBUTE is either an attribute, either an comma separate attribute
33enclose into bracket, either a '*' to match any attribute.
34
35Special keyword C<@CREATE> and C<@DELETE> can be used to allow or deny object
36creation and deletion. In this case read permission and USER in form C<$...> for
37C<@CREATE> have no effect (see below). C<*> do not include C<@CREATE> and
38C<@DELETE> action.
39
40=item USER can be
41
42=over 4
43
44=item a username
45
46=item group, prefixed by a '%'
47
48=item an attribute of the accessed object which should contains the username,
49prefixed by '$'
50
51=item C<*> to math any user
52
53=item @authenticated or @anonymous for any authenticated user and non
54authenticated user ('*' include both)
55
56=back
57
58=item Permission are read and or write, or deny.
59
60=back
61
62ACL are applied in the order they appear in the file.
63
64=cut
65
66sub read_acl_file {
67    my ($self, $file) = @_;
68
69    my $acl;
70    my $line_nb = 0;
71    my $prevline = "";
72    if (open(my $handle, '<', $file)) {
73        while(my $realline = <$handle>) {
74            $line_nb++;
75            chomp($realline);
76            if ($realline =~ /\\$/) {
77                # continuation line
78                $prevline .= $realline;
79                $prevline =~ s/\s*\\$//;
80                next;
81            }
82            my $line = $prevline . $realline; # keep track to report malformed file
83            $prevline = "";
84            $line =~ s/\s*#.*//;
85            if ($line =~ /^(\S.*)/) {
86                if ($acl) {
87                    push(@{$self->{_acls}}, $acl);
88                }
89                $acl = LATMOS::Accounts::Acls::acl->new($1) or do {
90                    la_log(LA_ERR, "Error in %s at line %d", $file,
91                        $line_nb);
92                    return;
93                };
94            } elsif ($line =~ /^\s+(\S.*)/) {
95                if ($acl) {
96                    $acl->add_perm($line) or do {
97                        la_log(LA_ERR, "Error in %s at line %d", $file,
98                            $line_nb);
99                        return;
100                    };
101                } else {
102                    # err no acl
103                    return;
104                }
105            } elsif ($line =~ /^\s*$/) {
106                # just empty line
107            } else {
108            }
109        }
110        if ($acl) {
111            push(@{$self->{_acls}}, $acl);
112        }
113    } else {
114        la_log(LA_ERR, "Cannot open acl file %s", $file);
115        return;
116    }
117    1;
118}
119
120sub add {
121    my ($self, $obj, $list) = @_;
122    my $acl = LATMOS::Accounts::Acls::acl->new(
123        $obj, $list) or return;
124    push(@{$self->{_acls}}, $acl);
125    return 1;
126}
127
128sub check {
129    my ($self, $obj, $attr, $perm, $who, $groups) = @_;
130    # Asking 'r' perm over create or delete has no sense:
131    $attr =~ /^@(CREATE|DELETE)$/ && $perm eq 'r' and return;
132
133    foreach my $acl (@{$self->{_acls}}) {
134        my $res = $acl->match($obj, $attr, $perm, $who, $groups);
135        defined($res) and return $res;
136    }
137    return 0;
138}
139
140sub dump {
141    my ($self) = @_;
142    foreach my $acl (@{$self->{_acls}}) {
143        print $acl->dump, "\n";
144    }
145}
146
147package LATMOS::Accounts::Acls::acl;
148use strict;
149use warnings;
150
151sub new {
152    my ($class, $objdotatt, $list) = @_;
153    my ($obj, $attr) = $objdotatt =~ /^([^.]+)\.(.*)/ or return;
154    my @attrs = ($attr =~ /^\{(.*)\}$/)
155        ? (split(/\s*,\s*/, $1))
156        : ($attr);
157    @attrs or return;
158    my $acl = {
159        obj => lc($obj),
160        attr => [ map { lc } @attrs ],
161        users => [], # user->{r} = 1
162    };
163    bless($acl, $class);
164    foreach my $k (@{ $list || [] }) {
165        $acl->add_perm($k) or return;
166    }
167    $acl
168}
169
170sub add_perm {
171    my ($self, $perm) = @_;
172    my ($username, $perms) = $perm =~ /^\s*(\S*):\s*(.*)$/;
173    $username && $perms or return;
174    my $user = { user => $username };
175    foreach (split(/\s*,\s*/, $perms)) {
176        /^read$/i  and do { $user->{r} = 1; next; };
177        /^write$/i and do { $user->{w} = 1; next; };
178        /^deny$/i and do {
179            # reseting...
180            $user->{r} = 0;
181            $user->{w} = 0;
182            last; # we end here, life is hard
183        };
184        return;
185    }
186    push(@{$self->{users}}, $user);
187    1
188}
189
190sub match {
191    my ($self, $obj, $attr, $perm, $who, $groups) = @_;
192    my $objtype = ref $obj ? lc($obj->type) : $obj;
193    $attr = lc($attr);
194
195    # Does this ACL series concern this object:
196    if (!($self->{obj} eq '*' || $self->{obj} eq $objtype)) {
197        return
198    }
199    # Does this ACL series concern this attribute:
200    grep { $_ eq '*' || $_ eq $attr } @{$self->{attr}} or return;
201
202    # Foreach user, testing if this permission match:
203    foreach my $u (@{ $self->{users} }) {
204        # Obj have attr eq login user
205        if (substr($u->{user}, 0, 1) eq '$') { # check attr content
206            if (ref $obj) {
207                my $attribute = substr($u->{user}, 1);
208                my $val = $obj->_get_c_field($attribute) or return;
209                my @vals = ref $val ? (@{ $val }) : ($val);
210                return $u->{$perm} if (defined($u->{$perm}) && grep { $_ eq $who } @vals);
211            }
212        # user is in group
213        } elsif (substr($u->{user}, 0, 1) eq '%') { # group
214            my $group = substr($u->{user}, 1);
215            return $u->{$perm} if (defined($u->{$perm}) && grep { $group eq $_ } grep { $_ } @{$groups ||[]});
216        # any user
217        } elsif ($u->{user} eq '*' || $u->{user} eq $who) {
218            return $u->{$perm} if (defined($u->{$perm}));
219        # any authenticated user
220        } elsif (lc($u->{user}) eq '@authenticated' && $who) {
221            return $u->{$perm} if (defined($u->{$perm}));
222        # not login
223        } elsif (lc($u->{user}) eq '@anonymous' && $who eq "") {
224            return $u->{$perm} if (defined($u->{$perm}));
225        }
226    }
227    return;
228}
229
230sub dump {
231    my ($self) = @_;
232    my $dump = sprintf("%s.{%s}\n", $self->{obj}, join(', ', @{$self->{attr}}));
233    foreach my $u (@{ $self->{users} }) {
234        $dump .= sprintf("\t%s: %s\n",
235            $u->{user},
236            join(', ', ($u->{r} ? 'read' : ()), ($u->{w} ? 'write' : ())) ||
237            'deny');
238    }
239    $dump
240}
241
2421;
243
244__END__
Note: See TracBrowser for help on using the repository browser.