source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Unix.pm @ 430

Last change on this file since 430 was 430, checked in by nanardon, 15 years ago
  • kill useless tag managment
  • Property svn:keywords set to Id Rev
File size: 9.8 KB
Line 
1package LATMOS::Accounts::Bases::Unix;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases);
8use LATMOS::Accounts::Log;
9
10our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
11
12=head1 NAME
13
14LATMOS::Ad - Perl extension for blah blah blah
15
16=head1 SYNOPSIS
17
18  use LATMOS::Accounts::Bases;
19  my $base = LATMOS::Accounts::Bases->new('unix');
20  ...
21
22=head1 DESCRIPTION
23
24Account base access over standard unix file format.
25
26=head1 FUNCTIONS
27
28=cut
29
30=head2 new(%options)
31
32Create a new LATMOS::Ad object for windows AD $domain.
33
34domain / server: either the Ad domain or directly the server
35
36ldap_args is an optionnal list of arguments to pass to L<Net::LDAP>.
37
38=cut
39
40sub new {
41    my ($class, %options) = @_;
42   
43    my $base = {
44        passwd =>  $options{passwd} || '/etc/passwd',
45        shadow =>  $options{shadow} || '/etc/shadow',
46        group =>   $options{group} || '/etc/group',
47        gshadow => $options{gshadow} || '/etc/gshadow',
48        # are we using shadow, default to yes
49        use_shadow => (defined($options{use_shadow}) ? $options{use_shadow} : 1),
50        min_gid => $options{min_gid},
51        min_uid => $options{min_uid},
52        users => {},
53        groups => {},
54    };
55
56    bless($base, $class);
57}
58
59sub _canonicals_fields {
60    my ($self, $type, $for) = @_;
61    $type = lc($type);
62    {
63        user => {
64            uidNumber       => 'uid',
65            gidNumber       => 'gid',
66            gecos           => 'gecos',
67            homeDirectory   => 'home',
68            loginShell      => 'shell',
69            userPassword    => ($self->{use_shadow} ? 'spassword' : 'password'),
70            memberOf        => 'memberOf',
71            locked          => 'locked',
72            ($for !~ /w/ ? (
73            givenName       => 'givenName',
74            sn              => 'sn',
75            uid             => 'login',
76            sAMAccountName  => 'login',
77            ) : ()),
78            $self->{use_shadow} ?
79            (
80            shadowLastChange => 'last_changed',
81            shadowMin       => 'before_ch',
82            shadowMax       => 'after_ch',
83            shadowWarning   => 'exp_warn',
84            shadowInactive  => 'exp_disable',
85            shadowExpire    => 'disable',
86            shadowFlag      => 'res',
87            ) : (), 
88            # description => not supported
89        },
90        group => {
91            ($for !~ /w/ ? (
92            sAMAccountName  => 'group_name',
93            ) : ()),
94            gidNumber       => 'gid',
95            memberUID       => 'user_list',
96        },
97    }->{$type}
98}
99
100sub list_canonical_fields {
101    my ($self, $type, $for) = @_;
102    $for ||= 'rw';
103    keys %{ $self->_canonicals_fields($type, $for) || {} }
104}
105
106sub get_field_name {
107    my ($self, $type, $cfield, $for) = @_;
108    $for ||= 'rw';
109    ($self->_canonicals_fields($type, $for) || {})->{$cfield}
110}
111
112my @password_fields = qw(account password uid gid gecos home shell);
113my @shadow_fields =   qw(account spassword last_changed before_ch after_ch exp_warn exp_disable disable res);
114my @group_fields =    qw(group_name passwd gid user_list);
115my @gshadow_fields =  qw(group_name spassword unknown suser_list);
116
117# All UNIX account file are colon separated field
118# This function factorize open/read/split fields
119
120sub _load_unix_file {
121    my ($self, $file, $callback) = @_;
122    open(my $handle, '<', $file) or do {
123        la_log(LA_ERR, "Cannot open unix file `%s' for reading (%s)", $file, $!);
124        return;
125    };
126    while (my $line = <$handle>) {
127        chomp($line);
128        my @ch = split(':', $line);
129        $callback->(@ch);
130    }
131    close($handle);
132    return 1;
133}
134
135=head2 load
136
137Read file and load data into memory
138
139=cut
140
141sub load {
142    my ($self) = @_;
143   
144    $self->_load_unix_file(
145        $self->{passwd},
146        sub {
147            my @ch = @_;
148            my $user = $ch[0] or return;
149            # TODO add check ?
150            foreach (@password_fields) {
151                $self->{users}{$user}{$_} = shift(@ch);
152            }
153            if ($self->{users}{$user}{password} =~ /^!!/) {
154                $self->{users}{$user}{password} =~ s/^!!//;
155                $self->{users}{$user}{locked} = 1;
156            }
157            $self->{users}{$user}{shell} =~ s/^-//;
158        },
159    ) or return;
160   
161    $self->_load_unix_file(
162        $self->{group},
163        sub {
164            my @ch = @_;
165            my $group = $ch[0];
166            # TODO add check ?
167            foreach (@group_fields) {
168                $self->{groups}{$group}{$_} = shift(@ch);
169            }
170            # split user in the group
171            foreach (split(',', ($self->{groups}{$group}{'user_list'} || ''))) {
172                $self->{groups}{$group}{'users'}{$_} = 1;
173            }
174        }
175    ) or return;
176
177    # using shadow ? then reading shadow file
178    if ($self->{use_shadow}) {
179
180    $self->_load_unix_file(
181        $self->{shadow},
182        sub {
183            my @ch = @_;
184            my $user = $ch[0];
185            foreach (@shadow_fields) {
186                $self->{users}{$user}{$_} = shift(@ch);
187            }
188            if ($self->{users}{$user}{spassword} =~ /^!!/) {
189                $self->{users}{$user}{spassword} =~ s/^!!//;
190                $self->{users}{$user}{locked} = 1;
191            }
192        }
193    ) or return;
194
195    $self->_load_unix_file(
196        $self->{gshadow},
197        sub {
198            my @ch = @_;
199            my $group = $ch[0];
200            # TODO add check ?
201            foreach (@gshadow_fields) {
202                $self->{groups}{$group}{$_} = shift(@ch);
203            }
204            # split user in the group
205            foreach (split(',', $self->{groups}{$group}{'suser_list'} || '')) {
206                $self->{groups}{$group}{'susers'}{$_} = 1;
207            }
208        }
209    ) or return;
210
211    } # use shadow ?
212
213    1;
214}
215
216sub _save_unix_file {
217    my ($self, $file, @data) = @_;
218    open(my $handle, '>', $file) or do {
219        la_log(LA_ERR, "Cannot open unix file `%s' for writing (%s)", $file, $!);
220        return;
221    };
222    foreach my $line (@data) {
223        print $handle join(':', map { defined($_) ? $_ : '' } @$line) . "\n";
224    }
225    close($handle);
226    return 1;
227}
228
229sub _commit {
230    my ($self) = @_;
231
232    $self->_save_unix_file(
233        $self->{passwd},
234        map {[
235            $_,
236            ($self->{users}{$_}{locked}
237                ? '!!' . ($self->{users}{$_}{password} || '')
238                : ($self->{users}{$_}{password} || 'x')), # No empty pass !!
239            $self->{users}{$_}{uid},
240            $self->{users}{$_}{gid},
241            $self->{users}{$_}{gecos} || '',
242            $self->{users}{$_}{home} || '/dev/null',
243            ($self->{users}{$_}{locked} ? '-' : '') . ($self->{users}{$_}{shell}
244                || '/bin/false'),
245        ]} sort { $self->{users}{$a}{uid} <=> $self->{users}{$b}{uid} } keys %{$self->{users}}
246    ) or return;
247
248    $self->_save_unix_file(
249        $self->{group},
250        map {[
251            $_,
252            $self->{groups}{$_}{password} || 'x',
253            $self->{groups}{$_}{gid},
254            join(',', keys %{$self->{groups}{$_}{users} || {}}), 
255        ]} sort {  $self->{groups}{$a}{gid} <=> $self->{groups}{$b}{gid} } keys %{$self->{groups}}
256    ) or return;
257
258    if ($self->{use_shadow}) {
259
260    $self->_save_unix_file(
261        $self->{shadow},
262        map {[
263            $_,
264            ($self->{users}{$_}{locked}
265                ? '!!' . ($self->{users}{$_}{spassword} || '')
266                : $self->{users}{$_}{spassword} || 'x'),
267            $self->{users}{$_}{last_changed},
268            $self->{users}{$_}{before_ch},
269            $self->{users}{$_}{after_ch},
270            $self->{users}{$_}{exp_warn},
271            $self->{users}{$_}{exp_disable},
272            $self->{users}{$_}{disable},
273            $self->{users}{$_}{res},
274        ]} sort { $self->{users}{$a}{uid} <=> $self->{users}{$b}{uid} } keys %{$self->{users}}
275    ) or return;
276
277
278    $self->_save_unix_file(
279        $self->{gshadow},
280        map {[
281            $_,
282            $self->{groups}{$_}{spassword} || 'x',
283            $self->{groups}{$_}{unknown},
284            join(',', keys %{$self->{groups}{$_}{susers} || {}}), 
285        ]} sort {  $self->{groups}{$a}{gid} <=> $self->{groups}{$b}{gid} } keys %{$self->{groups}}
286    ) or return;
287
288    } # If use shadow
289
290    1
291}
292
293sub list_objects {
294    my ($self, $otype) = @_;
295
296    # objects are store into sub ref:
297    my $internal_obj = {
298        user => 'users',
299        group => 'groups',
300    }->{$otype};
301    my @obj = sort keys %{$self->{$internal_obj} || {} };
302    for ($otype) {
303        /^user$/ and
304            return grep { $self->{$internal_obj}{$_}{uid} >= ($self->{min_uid} || 0) } @obj;
305        /^group$/ and
306            return grep { $self->{$internal_obj}{$_}{gid} >= ($self->{min_gid} || 0) } @obj;
307    }
308}
309
310sub create_object {
311    my ($self, $otype, $id, %data) = @_;
312
313    # objects are store into sub ref:
314    my $internal_obj = {
315        user => 'users',
316        group => 'groups',
317    }->{$otype};
318    for ($otype) {
319        /^user$/ && !defined($data{uid}) && !defined($data{gid}) and return;
320        /^group$/ && !defined($data{gid}) and return;
321    }
322
323    if ($self->{$internal_obj}{$id}) { return };
324    $self->{$internal_obj}{$id} = {
325        account => $id,
326        uid => $data{uid},
327        gid => $data{gid},
328    };
329    my $obj = $self->get_object($otype, $id) or return;
330    $obj->set_fields(%data);
331    $obj
332}
333
334sub delete_object {
335    my ($self, $otype, $id, %data) = @_;
336
337    # objects are store into sub ref:
338    my $internal_obj = {
339        user => 'users',
340        group => 'groups',
341    }->{$otype};
342    delete $self->{$internal_obj}{$id};
343    1
344}
345
3461;
347
348__END__
349
350=head1 SEE ALSO
351
352=head1 AUTHOR
353
354Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
355
356=head1 COPYRIGHT AND LICENSE
357
358Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
359
360This library is free software; you can redistribute it and/or modify
361it under the same terms as Perl itself, either Perl version 5.10.0 or,
362at your option, any later version of Perl 5 you may have available.
363
364
365=cut
Note: See TracBrowser for help on using the repository browser.