source: LATMOS-Accounts/lib/LATMOS/Accounts/Acls.pm @ 479

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