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

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