source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql/User.pm @ 861

Last change on this file since 861 was 861, checked in by nanardon, 13 years ago
  • reimport missing files from previous svn
  • Property svn:keywords set to Id Rev
File size: 22.6 KB
Line 
1package LATMOS::Accounts::Bases::Sql::User;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use LATMOS::Accounts::Utils;
8use LATMOS::Accounts::Log;
9use base qw(LATMOS::Accounts::Bases::Sql::objects);
10
11our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
12
13=head1 NAME
14
15LATMOS::Ad - Perl extension for blah blah blah
16
17=head1 SYNOPSIS
18
19  use LATMOS::Accounts::Bases;
20  my $base = LATMOS::Accounts::Bases->new('sql');
21  ...
22
23=head1 DESCRIPTION
24
25Account base access over standard unix file format.
26
27=head1 FUNCTIONS
28
29=cut
30
31=head2 new(%options)
32
33Create a new LATMOS::Ad object for windows AD $domain.
34
35domain / server: either the Ad domain or directly the server
36
37ldap_args is an optionnal list of arguments to pass to L<Net::LDAP>.
38
39=cut
40
41sub object_table { 'user' }
42
43sub key_field { 'name' }
44
45sub has_extended_attributes { 1 }
46
47sub _get_attr_schema {
48    my ($class, $base) = @_;
49
50    $class->SUPER::_get_attr_schema($base,
51        {
52            uidNumber => { inline => 1, iname => 'uidnumber', uniq => 1,
53                mandatory => 1, },
54            uidnumber => { inline => 1, hide => 1, },
55            gidNumber => { 
56                inline => 1,
57                iname => 'gidnumber',
58                mandatory => 1,
59                can_values => sub {
60                    map { $base->get_object('group',
61                            $_)->get_attributes('gidNumber') }
62                    $base->list_objects('group')
63                },
64                display => sub {
65                    my ($self, $val) = @_;
66                    my ($gr) = $self->base->search_objects('group', "gidNumber=$val")
67                        or return;
68                    return $gr;
69                },
70                reference => 'group',
71            },
72            loginShell => { mandatory => 1 },
73            gidnumber => { inline => 1, hide => 1,
74                can_values => sub {
75                    map { $_->get_attributes('gidNumber') }
76                    map { $base->get_object('group', $_) }
77                    $base->list_objects('group')
78                },
79                mandatory => 1,
80                reference => 'group',
81            },
82            exported  => {
83                inline => 1,
84                formtype => 'CHECKBOX',
85            },
86            locked    => {
87                formtype => 'CHECKBOX',
88                formopts => { rawvalue => 1, },
89            },
90            expire    => { inline => 1, formtype => 'DATE', },
91            name      => { inline => 1, ro => 1, },
92            cn        => { inline => 1, ro => 1, iname => 'name' },
93            create    => { inline => 1, ro => 1, },
94            date      => { inline => 1, ro => 1, },
95            memberOf  => { multiple => 1, delayed => 1, },
96            forward   => {},
97            aliases   => {
98                reference => 'aliases',
99                formtype => 'TEXT',
100                multiple => 1,
101            },
102            revaliases => {
103                reference => 'revaliases',
104                formtype => 'TEXT',
105            },
106            manager => {
107                delayed => 1,
108                can_values => sub {
109                    my %uniq = map { $_ => 1 } grep { $_ }
110                    ($_[1] ? $_[1]->get_attributes('manager') : ()),
111                    $base->search_objects('user', 'active=*');
112                    sort keys %uniq;
113                },
114                reference => 'user',
115            },
116            department => {
117                reference => 'group',
118                can_values => sub {
119                    $base->search_objects('group', 'sutype=dpmt')
120                }
121            },
122            contratType => {
123                reference => 'group',
124                can_values => sub {
125                    $base->search_objects('group', 'sutype=contrattype')
126                }
127            },
128            site => {
129                reference => 'site',
130                can_values => sub {
131                    $base->search_objects('site')
132                }
133            },
134            co => { },
135            l => { },
136            postalCode => { },
137            streetAddress => { formtype => 'TEXTAREA', },
138            postOfficeBox => { },
139            st => { },
140            facsimileTelephoneNumber => { },
141            o => { },
142            telephoneNumber => { },
143            physicalDeliveryOfficeName => { },
144            uid => { iname => 'name', ro => 1 },
145            cn =>  { iname => 'name', ro => 1 },
146            gecos => { ro => 1, },
147            displayName  => { ro => 1, managed => 1, },
148            sAMAccountName  => { ro => 1, managed => 1  },
149            accountExpires => { ro => 1, managed => 1 },
150            shadowExpire => { ro => 1, managed => 1 },
151            directReports => {
152                reference => 'user',
153                ro => 1,
154                delayed => 1,
155            },
156            managedObjects => { ro => 1, reference => 'group', },
157            otheraddress => { ro => 1, reference => 'address', },
158            mainaddress => { ro => 1, reference => 'address', },
159            postalAddress => { ro => 1, },
160            facsimileTelephoneNumber => { ro => 1, },
161            allsite   => {
162                ro => 1,
163                reference => 'site',
164            },
165            managerContact => {
166                ro => 1,
167                reference => 'user',
168            },
169            expireText => { ro => 1, },
170            krb5ValidEnd => { ro => 1, },
171            cells  => {
172                ro => 1,
173                reference => 'group',
174            },
175            departments => {
176                reference => 'group',
177                delayed => 1,
178                ro => 1,
179            },
180            arrivalDate => { },
181            expired => { ro => 1 },
182            active => { ro => 1 },
183                pwdAccountLockedTime => { managed => 1, ro => 1 }
184        }
185    )
186}
187
188sub get_field {
189    my ($self, $field) = @_;
190    if ($field eq 'gecos') {
191        my $gecos = sprintf("%s,%s,%s,%s",
192            join(' ', grep { $_ } 
193                    ($self->_get_c_field('givenName'),
194                    ($self->_get_c_field('sn'))))
195                || $self->_get_c_field('description') || '',
196            join(' - ', grep { $_ } (($self->_get_c_field('site') ||
197                        $self->_get_c_field('l')),
198                $self->_get_c_field('physicalDeliveryOfficeName'))) || '',
199            $self->_get_c_field('telephoneNumber') || '',
200            $self->_get_c_field('expireText') || '',
201        );
202        $gecos =~ s/:/ /g;
203        return to_ascii($gecos);
204    } elsif ($field eq 'displayName') {
205        return join(' ', grep { $_ } ($self->_get_c_field('givenName'), ($self->_get_c_field('sn'))))
206            || $self->_get_c_field('description') || $self->id;
207    } elsif ($field eq 'sAMAccountName') {
208        return $self->id;
209    } elsif ($field eq 'memberOf') {
210        my $sth = $self->db->prepare_cached(
211            q{
212            select name from "group" join
213            group_attributes on group_attributes.okey = "group".ikey
214            where value = ? and attr = ?
215            }
216        );
217        $sth->execute($self->id, 'memberUID');
218        my @res;
219        while (my $res = $sth->fetchrow_hashref) {
220            push(@res, $res->{name});
221        }
222        return \@res;
223    } elsif ($field eq 'directReports') {
224        my $sth = $self->db->prepare_cached(
225            q{
226            select name from "user" join
227            user_attributes on user_attributes.okey = "user".ikey
228            where value = ? and attr = ?
229            }
230        );
231        $sth->execute($self->id, 'manager');
232        my @res;
233        while (my $res = $sth->fetchrow_hashref) {
234            push(@res, $res->{name});
235        }
236        return \@res;
237    } elsif ($field eq 'accountExpires') {
238        my $sth = $self->db->prepare_cached(
239            sprintf(
240                q{select extract(epoch from expire) + 11644474161 as expire
241                from %s where %s = ?},
242                $self->db->quote_identifier($self->object_table),
243                $self->db->quote_identifier($self->key_field),
244            )
245        );
246        $sth->execute($self->id);
247        my $res = $sth->fetchrow_hashref;
248        $sth->finish;
249        return $res->{expire} ? sprintf("%.f", $res->{expire} * 1E7) : '9223372036854775807';
250    } elsif ($field eq 'shadowExpire') {
251        my $sth = $self->db->prepare_cached(
252            sprintf(
253                q{select justify_hours(expire - '1/1/1970'::timestamp) as expire
254                from %s where %s = ?},
255                $self->db->quote_identifier($self->object_table),
256                $self->db->quote_identifier($self->key_field),
257            )
258        );
259        $sth->execute($self->id);
260        my $res = $sth->fetchrow_hashref;
261        $sth->finish;
262        return -1 unless($res->{expire});
263        $res->{expire} =~ /(\d+) days\s*(\w)?/;
264        return $1 + ($2 ? 1 : 0);
265    } elsif ($field eq 'krb5ValidEnd') {
266        my $sth = $self->db->prepare_cached(
267            sprintf(
268                q{select date_part('epoch', expire)::int as expire
269                from %s where %s = ?},
270                $self->db->quote_identifier($self->object_table),
271                $self->db->quote_identifier($self->key_field),
272            )
273        );
274        $sth->execute($self->id);
275        my $res = $sth->fetchrow_hashref;
276        $sth->finish;
277        return $res->{expire}
278    } elsif ($field eq 'expireText') {
279        my $sth = $self->db->prepare_cached(
280            sprintf(
281                q{select to_char(expire, 'YYYY/MM/DD') as expire
282                from %s where %s = ?},
283                $self->db->quote_identifier($self->object_table),
284                $self->db->quote_identifier($self->key_field),
285            )
286        );
287        $sth->execute($self->id);
288        my $res = $sth->fetchrow_hashref;
289        $sth->finish;
290        return $res->{expire}
291    } elsif ($field eq 'pwdAccountLockedTime') {
292        if ($self->_get_c_field('locked')) {
293            return '000001010000Z';
294        } else {
295            my $sth = $self->db->prepare_cached(
296                sprintf(
297                    q{select to_char(expire AT TIME ZONE 'Z', 'YYYYMMDDHH24MISSZ') as expire
298                    from %s where %s = ? and expire < now()},
299                    $self->db->quote_identifier($self->object_table),
300                    $self->db->quote_identifier($self->key_field),
301                )
302            );
303            $sth->execute($self->id);
304            my $res = $sth->fetchrow_hashref;
305            $sth->finish;
306            return $res->{expire}
307        }
308    } elsif ($field eq 'otheraddress') {
309        my $sth = $self->db->prepare_cached(q{
310            select name from address where "user" = ?
311        });
312        $sth->execute($self->id);
313        my @values;
314        while (my $res = $sth->fetchrow_hashref) {
315            push(@values, $res->{name});
316        }
317        return \@values;
318    } elsif ($field eq 'mainaddress') {
319        my $sth = $self->db->prepare_cached(q{
320            select name from address join address_attributes on ikey = okey
321            where "user" = ? and attr = 'isMainAddress'
322            });
323        $sth->execute($self->id);
324        my $res = $sth->fetchrow_hashref;
325        $sth->finish;
326        return $res->{name};
327    } elsif (grep { $field eq $_ } qw(postalAddress
328            co l postalCode streetAddress
329            postOfficeBox st
330            facsimileTelephoneNumber
331            o telephoneNumber
332            physicalDeliveryOfficeName
333            site
334        )) {
335        if (my $fmainaddress = $self->_get_c_field('mainaddress')) {
336            my $address = $self->base->get_object('address', $fmainaddress);
337            if ($address) {
338                return $address->_get_c_field($field);
339            } else { # can't happend
340                return;
341            }
342        } else {
343            return $self->SUPER::get_field($field);
344        }
345    } elsif ($field eq 'aliases') {
346        my $sth = $self->db->prepare(q{
347            select name from aliases where array[lower($1)] =
348                string_to_array(lower(array_to_string("forward", ',')), ',')
349        } . ($self->base->{wexported} ? '' : 'and exported = true'));
350        $sth->execute($self->id);
351        my @values;
352        while (my $res = $sth->fetchrow_hashref) {
353            push(@values, $res->{name});
354        }
355        return \@values;
356    } elsif ($field eq 'forward') {
357        my $sth = $self->db->prepare(q{
358            select forward from aliases where name = ?
359        } . ($self->base->{wexported} ? '' : ' and exported = true'));
360        $sth->execute($self->id);
361        my $res = $sth->fetchrow_hashref;
362        $sth->finish;
363        return $res->{forward}
364    } elsif ($field eq 'revaliases') {
365        my $sth = $self->db->prepare(q{
366            select "as" from revaliases where name = ?
367        } . ($self->base->{wexported} ? '' : ' and exported = true'));
368        $sth->execute($self->id);
369        my $res = $sth->fetchrow_hashref;
370        $sth->finish;
371        return $res->{as}
372    } elsif ($field eq 'managerContact') {
373        if (my $manager = $self->_get_c_field('manager')) {
374            return $manager;
375        } elsif (my $department = $self->_get_c_field('department')) {
376            my $obj = $self->base->get_object('group', $department);
377            return $obj->_get_c_field('managedBy');
378        } else {
379            return;
380        }
381    } else {
382        return $self->SUPER::get_field($field);
383    }
384}
385
386sub _get_state {
387    my ($self, $state) = @_;
388    for ($state) {
389        /^expired$/ and do {
390            my $attribute = $self->attribute('expire');
391            $attribute->check_acl('r') or return;
392            my $sth = $self->db->prepare_cached(
393                q{ select coalesce(expire < now(), false) as exp from "user"
394                where "user".name = ?}
395            );
396            $sth->execute($self->id);
397            my $res = $sth->fetchrow_hashref;
398            $sth->finish;
399            return $res->{exp} ? 1 : 0;
400        };
401    }
402}
403
404sub set_fields {
405    my ($self, %data) = @_;
406    my %fdata;
407    my $res = 0;
408    foreach my $attr (keys %data) {
409        $attr eq 'gidnumber' && $data{$attr} !~ /^\d+$/ and do {
410            my $group = $self->base->get_object('group', $data{$attr}) or do {
411                $self->base->log(LA_ERROR,
412                    "Can't set gidNumber to %s: no such group", $data{$attr});
413                return;
414            };
415            $data{$attr} = $group->get_attributes('gidNumber');
416        };
417        $attr =~ /^memberOf$/ and do {
418            my %member;
419            foreach (@{ $self->get_field('memberOf') }) {
420                $member{$_}{c} = 1;
421            }
422            foreach (grep { $_ } ref $data{$attr} ? @{ $data{$attr} || []} : $data{$attr}) {
423                $member{$_}{n} = 1;
424            }
425
426            foreach (keys %member) {
427                $member{$_}{c} && $member{$_}{n} and next; # no change !
428                my $group = $self->base->get_object('group', $_) or next;
429                ($group->_get_c_field('sutype') || '') =~ /^(jobtype|contrattype)$/ and next;
430                if ($member{$_}{n}) {
431                    my $sth = $self->db->prepare_cached(
432                        q{insert into group_attributes_users (value, attr, okey) values (?,?,?)}
433                    );
434                    $sth->execute($self->id, 'memberUID', $group->_get_ikey);
435                    $res++;
436                } elsif ($member{$_}{c}) {
437                    if (($self->get_c_field('department') || '') eq $group->id) {
438                        $self->base->log(LA_WARN,
439                            "Don't removing user %s from group %s: is it's department",
440                            $self->id, $group->id);
441                        next;
442                    }
443                    my $sth = $self->db->prepare_cached(
444                        q{delete from group_attributes_users where value = ? and attr = ? and okey = ?}
445                    );
446                    $sth->execute($self->id, 'memberUID', $group->_get_ikey);
447                    $res++;
448                } # else {} # can't happend
449            }
450            next;
451        };
452        $attr =~ /^forward$/ and do {
453            if ($data{$attr}) {
454                if (my $f = $self->base->get_object('aliases', $self->id)) {
455                    $res += $f->_set_c_fields(forward => $data{$attr});
456                } else {
457                    if ($self->base->_create_c_object('aliases', $self->id,
458                        forward => $data{$attr})) {
459                        $res++;
460                    } else {
461                        $self->base->log(LA_ERR, "Cannot add forward for %s",
462                            $self->id);
463                    }
464                }
465            } else {
466                if ($self->base->_delete_object('aliases', $self->id)) {
467                    $res++;
468                } else {
469                    $self->base->log(LA_ERR, "Cannot remove forward for %s",
470                        $self->id);
471                }
472            }
473            next;
474        };
475        $attr =~ /^aliases$/ and do {
476            my %aliases = map { $_ => 1 } grep { $_ } (ref $data{$attr} ? @{$data{$attr}} :
477                $data{$attr});
478            foreach ($self->_get_attributes('aliases')) {
479                $aliases{$_} ||= 0;
480                $aliases{$_} +=2;
481            }
482            foreach (keys %aliases) {
483                if ($aliases{$_} == 2) {
484                    if ($self->base->_delete_object('aliases', $_)) {
485                        $res++
486                    } else {
487                        $self->base->log(LA_ERR,
488                            "Cannot remove aliases %s from user %s", $_,
489                            $self->id);
490                    }
491                } elsif ($aliases{$_} == 1) {
492                    if ($self->base->_create_c_object('aliases', $_,
493                        forward => [ $self->id ])) {
494                        $res++
495                    } else {
496                        $self->base->log(LA_ERR, 'Cannot set forward %s to user %s',
497                            $_, $self->id);
498                        return
499                    }
500                } # 3 no change
501            }
502            next;
503        };
504        $attr =~ /^revaliases$/ and do {
505            if ($data{$attr}) {
506                if (my $obj = $self->base->
507                        get_object('revaliases', $self->id)) {
508                    my $ares = $obj->set_c_fields('as', $data{$attr});
509                    if (defined($ares)) {
510                        $res+=$ares;
511                    } else {
512                        $self->base->log(LA_ERR, 'Cannot set revaliases for user %s',
513                            $self->id);
514                    }
515                } else {
516                    if ($self->base->_create_c_object(
517                        'revaliases',
518                        $self->id, as => $data{$attr},
519                    )) {
520                        $res++;
521                    } else {
522                        $self->base->log(LA_ERR, 'Cannot set revaliases for user %s',
523                            $self->id);
524                    }
525                }
526            } else {
527                $self->base->_delete_object('revaliases', $self->id);
528                $res++;
529            }
530            next;
531        };
532        $attr =~ /^department$/ and do {
533            if ($data{$attr}) {
534                my $dpmt = $self->base->get_object('group', $data{$attr}) or do {
535                    $self->base->log(LA_ERR, 
536                        "Group %s does not exists",
537                        $data{$attr});
538                    return;
539                };
540                if ((($dpmt->_get_c_field('sutype') || '') ne 'dpmt')) {
541                    $self->base->log(LA_ERR, "Group %s is not a department",
542                        $data{$attr});
543                    return;
544                }
545            }
546        };         
547        $attr =~ /^jobType$/ and do {
548            if ($data{$attr}) {
549                my $dpmt = $self->base->get_object('group', $data{$attr}) or do {
550                    $self->base->log(LA_ERR, 
551                        "Group %s does not exists",
552                        $data{$attr});
553                    return;
554                };
555                if ((($dpmt->_get_c_field('sutype') || '') ne 'jobtype')) {
556                    $self->base->log(LA_ERR, "Group %s is not a jobtype",
557                        $data{$attr});
558                    return;
559                }
560            }
561        };         
562        $attr =~ /^contratType$/ and do {
563            if ($data{$attr}) {
564                my $dpmt = $self->base->get_object('group', $data{$attr}) or do {
565                    $self->base->log(LA_ERR, 
566                        "Group %s does not exists",
567                        $data{$attr});
568                    return;
569                };
570                if ((($dpmt->_get_c_field('sutype') || '') ne 'contrattype')) {
571                    $self->base->log(LA_ERR, "Group %s is not a contrattype",
572                        $data{$attr});
573                    return;
574                }
575            }
576        };         
577        grep { $attr eq $_ } (qw(co l postalCode streetAddress
578            postOfficeBox st facsimileTelephoneNumber
579            o telephoneNumber physicalDeliveryOfficeName site)) and do {
580            my $fmainaddress = $self->_get_c_field('mainaddress');
581            # set address attribute => create address object on the fly
582            # except if attr is empty !
583            if (!$fmainaddress && $data{$attr}) {
584                $fmainaddress = $self->id . '-' . join('', map { ('a'..'z')[rand(26)] }
585                (0..4));
586                $self->base->_create_c_object(
587                    'address', $fmainaddress,
588                    user => $self->id,
589                    isMainAddress => 1, ) or do {
590                    $self->base->log(LA_ERR,
591                        "Cannot create main address for user %s", $self->id);
592                    return;
593                };
594            }
595            if ($fmainaddress && 
596                (my $address = $self->base->get_object('address', $fmainaddress))) {
597                if ($address->attribute($attr) &&
598                    !$address->attribute($attr)->ro) {
599                    $res += $address->set_c_fields($attr => $data{$attr}) ||0;
600                }
601            }
602            next;
603        };
604        $fdata{$attr} = $data{$attr} || undef;
605    }
606    if (keys %fdata) {
607        if (defined(my $res2 = $self->SUPER::set_fields(%fdata))) {
608           return $res2 + $res;
609       } else {
610           return;
611       }
612    } else { return $res; }
613}
614
615
6161;
617
618__END__
619
620=head1 SEE ALSO
621
622=head1 AUTHOR
623
624Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
625
626=head1 COPYRIGHT AND LICENSE
627
628Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
629
630This library is free software; you can redistribute it and/or modify
631it under the same terms as Perl itself, either Perl version 5.10.0 or,
632at your option, any later version of Perl 5 you may have available.
633
634
635=cut
Note: See TracBrowser for help on using the repository browser.