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

Last change on this file since 1524 was 1524, checked in by nanardon, 9 years ago

Allow fine control on ro attributes

This patch allow to control why the status attributes become read-only when
using employment.

  • Property svn:keywords set to Id Rev
File size: 44.1 KB
Line 
1package LATMOS::Accounts::Bases::Sql::User;
2
3use 5.010000;
4use strict;
5use warnings;
6use overload '""' => 'stringify';
7
8use LATMOS::Accounts::Utils;
9use LATMOS::Accounts::Log;
10use POSIX qw(strftime);
11use Date::Parse qw(str2time);
12use DateTime;
13use DateTime::TimeZone;
14use base qw(LATMOS::Accounts::Bases::Sql::objects);
15
16our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
17
18=head1 NAME
19
20LATMOS::Ad - Perl extension for blah blah blah
21
22=head1 DESCRIPTION
23
24Account base access over standard unix file format.
25
26=head1 FUNCTIONS
27
28=cut
29
30=head2 new(%config)
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 _object_table { 'user' }
41
42sub _key_field { 'name' }
43
44sub _has_extended_attributes { 1 }
45
46sub stringify {
47    my ($self) = @_;
48
49    return join(' ', grep { $_ }
50        (
51            $self->get_field('givenName'),
52            $self->get_field('sn')
53        )
54    )
55    || $self->get_field('description')
56    || $self->id;
57}
58
59sub _get_attr_schema {
60    my ($class, $base) = @_;
61
62    my $subsetaddress = sub {
63        my ($self, $data) = @_;
64        my $fmainaddress = $self->object->_get_c_field('mainaddress');
65
66        # set address attribute => create address object on the fly
67        # except if attr is empty !
68        if (!$fmainaddress && $data) {
69            $fmainaddress = $self->object->id . '-' . join('', map { ('a'..'z')[rand(26)] }
70                (0..4));
71            $self->base->_create_c_object(
72                'address', $fmainaddress,
73                user => $self->object->id,
74                isMainAddress => 1, ) or do {
75                $self->base->log(LA_ERR,
76                    "Cannot create main address for user %s", $self->object->id);
77                return;
78            };
79        }
80        if ($fmainaddress && 
81            (my $address = $self->base->get_object('address', $fmainaddress))) {
82            if ($address->attribute($self->name) &&
83                !$address->attribute($self->name)->ro) {
84                return $address->set_c_fields($self->name => $data) ||0;
85            }
86        }
87    };
88
89    my $attrs = {
90            exported => {
91                post => sub {
92                    my ($self, $value) = @_;
93                    my $attr = $self->name;
94
95                    if (my $obj = $self->base->
96                            get_object('revaliases', $self->object->id)) {
97                        my $ares = $obj->set_c_fields(
98                            ($attr eq 'exported' ? 'exported' : 'unexported') => $value
99                        );
100                        if (!defined($ares)) {
101                            $self->base->log(LA_ERR,
102                                'Cannot set revaliases exported attribute for user %s',
103                                $self->object->id);
104                        }
105                    }
106                    my $must_expire = $self->name eq 'exported'
107                        ? ($value ? 0 : 1 )
108                        : ($value ? 1 : 0 );
109
110                    foreach my $al (grep { $_ } $self->object->get_attributes('aliases'), $self->object->id) {
111                        my $obj = $self->base->get_object('aliases', $al) or next;
112                        $obj->_set_c_fields(
113                            exported => $value,
114                        );
115                    }
116                    foreach my $al ($self->object->get_attributes('otheraddress')) {
117                        my $obj = $self->base->get_object('address', $al) or next;
118                        $obj->_set_c_fields(
119                            exported => $value,
120                        );
121                    }
122                },
123            },
124            uidNumber => {
125                inline => 1,
126                iname => 'uidnumber',
127                uniq => 1,
128                mandatory => 1,
129                formopts => { length => 7 },
130            },
131            uidnumber => { inline => 1, hide => 1, monitored => 1 },
132            gidNumber => {
133                inline => 1,
134                iname => 'gidnumber',
135                mandatory => 1,
136                can_values => sub {
137                    map { $_->id, $_->get_attributes('gidNumber') }
138                    map { $base->get_object('group', $_) }
139                    $base->list_objects('group')
140                },
141                can_values => sub {
142                    my $sth = $base->db->prepare_cached(
143                        q{
144                        select name, gidnumber from "group" where exported = true
145                        }
146                    );
147                    $sth->execute();
148                    my @res;
149                    while (my $res = $sth->fetchrow_hashref) {
150                        push(@res, $res->{name});
151                        push(@res, $res->{gidnumber});
152                    }
153                    return @res;
154                },
155                display => sub {
156                    my ($self, $val) = @_;
157                    my ($gr) = $self->base->search_objects('group', "gidNumber=$val")
158                        or return;
159                    return $gr;
160                },
161                input => sub {
162                    my ($val) = @_;
163                    $val =~ /^\d+$/ and return $val;
164                    my ($gr) = $base->search_objects('group', "name=$val") or return;
165                    return $base->get_object('group', $gr)->get_attributes('gidNumber');
166                },
167                reference => 'group',
168            },
169            loginShell => { mandatory => 1 },
170            gidnumber => { inline => 1, hide => 1,
171                can_values => sub {
172                    map { $_->id, $_->get_attributes('gidNumber') }
173                    map { $base->get_object('group', $_) }
174                    $base->list_objects('group')
175                },
176                display => sub {
177                    my ($self, $val) = @_;
178                    my ($gr) = $self->base->search_objects('group', "gidNumber=$val")
179                        or return;
180                    return $gr;
181                },
182                input => sub {
183                    my ($val) = @_;
184                    $val =~ /^\d+$/ and return $val;
185                    my ($gr) = $base->search_objects('group', "name=$val") or return;
186                    return $base->get_object('group', $gr)->get_attributes('gidNumber');
187                },
188                mandatory => 1,
189                reference => 'group',
190                monitored => 1,
191            },
192            locked    => {
193                formtype => 'CHECKBOX',
194                formopts => { rawvalue => 1, },
195                monitored => 1,
196            },
197            expire        => {
198                inline => 1,
199                formtype => 'DATETIME',
200                monitored => 1,
201            },
202            endcircuit    => {
203                inline => 1,
204                formtype => 'DATE',
205                monitored => 1,
206            },
207            endEmployment => {
208                formtype => 'DATETIME',
209                managed => 1,
210                ro => 1,
211                get => sub {
212                    my ($attr) = @_;
213                    my $self = $attr->object;
214                    $self->computeEndEmployment($self->base->config('employment_delay'));
215                },
216            },
217            endStrictEmployment => {
218                formtype => 'DATETIME',
219                managed => 1,
220                ro => 1,
221                get => sub {
222                    my ($attr) = @_;
223                    my $self = $attr->object;
224                    $self->computeEndEmployment();
225                },
226            },
227            endCurrentEmployment => {
228                formtype => 'DATETIME',
229                managed => 1,
230                ro => 1,
231                get => sub {
232                    my ($attr) = @_;
233                    my $self = $attr->object;
234
235                    my $list_empl = $self->base->db->prepare_cached(q{
236                        SELECT * FROM employment WHERE "user" = ? and
237                            firstday <= now() and
238                            (lastday is null or lastday >= now() - '1 days'::interval)
239                            order by firstday asc
240                    });
241                    $list_empl->execute($self->id);
242                    my $end;
243                    while (my $res = $list_empl->fetchrow_hashref) {
244                        if (!$res->{lastday}) {
245                            # Ultimate employment.
246                            $list_empl->finish;
247                            return undef;
248                        } else {
249                            $end = DateTime->from_epoch(epoch => str2time($res->{lastday}));
250                            $end->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
251                            $end->add(hours => 23, minutes => 59, seconds => 59);
252                        }
253                        last;
254                    }
255                    $list_empl->finish;
256
257                    return $end ? $end->iso8601 : undef
258                },
259            },
260            endLastEmployment => {
261                formtype => 'DATETIME',
262                managed => 1,
263                ro => 1,
264                get => sub {
265                    my ($attr) = @_;
266                    my $self = $attr->object;
267
268                    my $list_empl = $self->base->db->prepare_cached(q{
269                        SELECT * FROM employment WHERE "user" = ?
270                        order by lastday desc nulls first
271                    });
272                    $list_empl->execute($self->id);
273                    my $res = $list_empl->fetchrow_hashref;
274                    $list_empl->finish;
275                    my $end;
276                    if ($res && $res->{lastday}) {
277                        $end = DateTime->from_epoch(epoch => str2time($res->{lastday}));
278                        $end->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
279                        $end->add(hours => 23, minutes => 59, seconds => 59);
280                    }
281                    return $end ? $end->iso8601 : undef
282                },
283            },
284            cn        => {
285                inline => 1, ro => 1,
286                get => sub {
287                    my ($self) = @_;
288                    return join(' ', grep { $_ } 
289                        (
290                            $self->object->_get_c_field('givenName'),
291                            $self->object->_get_c_field('sn')
292                        )
293                    )
294                    || $self->object->_get_c_field('description')
295                    || $self->object->id;
296                },
297            },
298            memberOf  => {
299                reference => 'group',
300                multiple => 1, delayed => 1,
301                get => sub {
302                    my ($self) = @_;
303                    my $obj = $self->object;
304                    my $sth = $obj->db->prepare_cached(
305                        q{
306                        select name from "group" join
307                        group_attributes on group_attributes.okey = "group".ikey
308                        where value = ? and attr = ?
309                        } . ($self->base->{wexported} ? '' : ' and "group".exported = true')
310                    );
311                    $sth->execute($obj->id, 'memberUID');
312                    my @res;
313                    while (my $res = $sth->fetchrow_hashref) {
314                        push(@res, $res->{name});
315                    }
316                    return \@res;
317                },
318                set => sub {
319                    my ($self, $values) = @_;
320                    my %old = map { $_ => 'o' } @{ $self->get };
321                    foreach my $group (grep { $_ } ref $values ? @{ $values } : $values) {
322                        if ($old{$group}) {
323                            $old{$group} = undef; # no change
324                        } else {
325                            $old{$group} = 'n';
326                        }
327                    }
328
329                    my $res = 0;
330                    foreach my $group (keys %old) {
331                        $old{$group} or next; # no change
332
333                        my $ogroup = $self->base->get_object('group', $group) or next;
334                        ($ogroup->_get_c_field('sutype') || '') =~ /^(jobtype|contrattype)$/ and next;
335
336                        if ($old{$group} eq 'n') {
337                            $res += $ogroup->_addAttributeValue('memberUID', $self->object->id);
338                        } else {
339                            if (($self->object->_get_c_field('department') || '') eq $ogroup->id) {
340                                $self->base->log(LA_WARN,
341                                    "Don't removing user %s from group %s: is its department",
342                                    $self->object->id, $ogroup->id);
343                                next;
344                            }
345                            $res += $ogroup->_delAttributeValue('memberUID', $self->object->id);
346                        }
347                    }
348                    return $res;
349                },
350            },
351            forward => {
352                managed => 1,
353                get => sub {
354                    my ($self) = @_;
355                    my $sth = $self->base->db->prepare(q{
356                        select forward from aliases where name = ?
357                        } . ($self->base->{wexported} ? '' : ' and exported = true'));
358                    $sth->execute($self->object->id);
359                    my $res = $sth->fetchrow_hashref;
360                    $sth->finish;
361                    return $res->{forward}
362                },
363                set => sub {
364                    my ($self, $data) = @_;
365                    if ($data) {
366                        if (my $f = $self->base->get_object('aliases', $self->object->id)) {
367                            return $f->_set_c_fields(forward => $data);
368                        } else {
369                            if ($self->base->_create_c_object(
370                                    'aliases', $self->object->id,
371                                    forward => $data,
372                                    description => 'automatically created for ' . $self->object->id,
373                                )) {
374                                return 1;
375                            } else {
376                                $self->base->log(LA_ERR, "Cannot add forward for %s",
377                                    $self->object->id);
378                            }
379                        }
380                    } else {
381                        if (my $res = $self->base->_delete_object('aliases', $self->object->id)) {
382                            return $res;
383                        } else {
384                            $self->base->log(LA_ERR, "Cannot remove forward for %s",
385                                $self->object->id);
386                        }
387                    }
388                    return;
389                },
390            },
391            aliases   => {
392                #reference => 'aliases',
393                delayed => 1,
394                formtype => 'TEXT',
395                multiple => 1,
396                get => sub {
397                    my ($self) = @_;
398                    my $sth = $self->base->db->prepare(q{
399                        select name from aliases where lower($1) =
400                        lower(array_to_string("forward", ','))
401                        } . ($self->base->{wexported} ? '' : 'and exported = true'));
402                    $sth->execute($self->object->id);
403                    my @values;
404                    while (my $res = $sth->fetchrow_hashref) {
405                        push(@values, $res->{name});
406                    }
407                    return \@values;
408                },
409                set => sub {
410                    my ($self, $data) = @_;
411
412                    my $res = 0;
413                    my %aliases = map { $_ => 1 } grep { $_ } (ref $data ? @{$data} : $data);
414                    foreach ($self->object->_get_attributes('aliases')) {
415                        $aliases{$_} ||= 0;
416                        $aliases{$_} +=2;
417                    }
418                    foreach (keys %aliases) {
419                        if ($aliases{$_} == 2) {
420                            if ($self->base->_delete_object('aliases', $_)) {
421                                $res++
422                            } else {
423                                $self->base->log(LA_ERR,
424                                    "Cannot remove aliases %s from user %s", $_,
425                                    $self->object->id);
426                            }
427                        } elsif ($aliases{$_} == 1) {
428                            if ($self->base->_create_c_object(
429                                    'aliases', $_,
430                                    forward => [ $self->object->id ],
431                                    description => 'automatically created for ' . $self->object->id,
432                                )) {
433                                $res++
434                            } else {
435                                $self->base->log(LA_ERR, 'Cannot set forward %s to user %s',
436                                    $_, $self->object->id);
437                                return;
438                            }
439                        } # 3 no change
440                    }
441                    $res
442                },
443            },
444            revaliases => {
445                formtype => 'TEXT',
446                get => sub {
447                    my ($self) = @_;
448
449                    if (my $obj = $self->base->
450                        get_object('revaliases', $self->object->id)) {
451                        return $obj->get_attributes('as');
452                    } else {
453                        return;
454                    }
455                },
456                set => sub {
457                    my ($self, $data) = @_;
458               
459                    my $res = 0;
460                    if ($data) {
461                        if (my $obj = $self->base->
462                            get_object('revaliases', $self->object->id)) {
463                            my $ares = $obj->set_c_fields(
464                                'as' => $data,
465                                'exported' => ($self->object->get_attributes('exported') || 0),
466                            );
467                            if (defined($ares)) {
468                                $res+=$ares;
469                            } else {
470                                $self->base->log(LA_ERR, 'Cannot set revaliases for user %s',
471                                    $self->object->id);
472                            }
473                        } else {
474                            if ($self->base->_create_c_object(
475                                    'revaliases',
476                                    $self->object->id, as => $data,
477                                    'exported' => ($self->object->get_attributes('exported') || 0),
478                                    description => 'automatically created for ' . $self->object->id,
479                                )) {
480                                $res++;
481                            } else {
482                                $self->base->log(LA_ERR, 'Cannot set revaliases for user %s',
483                                    $self->object->id);
484                            }
485                        }
486                    } else {
487                        $self->base->_delete_object('revaliases', $self->object->id);
488                        $res++;
489                    }
490
491                    $res
492                },
493            },
494            manager => {
495                reference => 'user',
496                ro => 1,
497                get => sub {
498                    my ($self) = @_;
499                    if (my $manager = $self->object->_get_c_field('managerContact')) {
500                        return $manager;
501                    } elsif (my $department = $self->object->_get_c_field('department')) {
502                        my $obj = $self->base->get_object('group', $department);
503                        return $obj->_get_c_field('managedBy');
504                    } else {
505                        return;
506                    }
507                },
508            },
509            department => {
510                reference => 'group',
511                can_values => sub {
512                    $base->search_objects('group', 'sutype=dpmt')
513                },
514                monitored => 1,
515            },
516            contratType => {
517                reference => 'group',
518                can_values => sub {
519                    $base->search_objects('group', 'sutype=contrattype')
520                },
521                monitored => 1,
522            },
523            site => {
524                reference => 'site',
525                can_values => sub {
526                    $base->search_objects('site')
527                },
528                get => sub {
529                    my ($self) = @_;
530                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
531                        $self->base->get_object('address', $fmainaddress)
532                            ->_get_c_field($self->name);
533                    } else {
534                        return;
535                    }
536                },
537                set => $subsetaddress,
538            },
539            co => {
540                get => sub {
541                    my ($self) = @_;
542                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
543                        $self->base->get_object('address', $fmainaddress)
544                            ->_get_c_field($self->name);
545                    } else {
546                        return;
547                    }
548                },
549                set => $subsetaddress,
550            },
551            l => {
552                get => sub {
553                    my ($self) = @_;
554                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
555                        $self->base->get_object('address', $fmainaddress)
556                            ->_get_c_field($self->name);
557                    } else {
558                        return;
559                    }
560                },
561                set => $subsetaddress,
562            },
563            postalCode => {
564                get => sub {
565                    my ($self) = @_;
566                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
567                        $self->base->get_object('address', $fmainaddress)
568                            ->_get_c_field($self->name);
569                    } else {
570                        return;
571                    }
572                },
573                set => $subsetaddress,
574            },
575            streetAddress => {
576                formtype => 'TEXTAREA',
577                get => sub {
578                    my ($self) = @_;
579                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
580                        $self->base->get_object('address', $fmainaddress)
581                            ->_get_c_field($self->name);
582                    } else {
583                        return;
584                    }
585                },
586                set => $subsetaddress,
587            },
588            postOfficeBox => {
589                get => sub {
590                    my ($self) = @_;
591                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
592                        $self->base->get_object('address', $fmainaddress)
593                            ->_get_c_field($self->name);
594                    } else {
595                        return;
596                    }
597                },
598                set => $subsetaddress,
599            },
600            st => {
601                get => sub {
602                    my ($self) = @_;
603                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
604                        $self->base->get_object('address', $fmainaddress)
605                            ->_get_c_field($self->name);
606                    } else {
607                        return;
608                    }
609                },
610                set => $subsetaddress,
611            },
612            facsimileTelephoneNumber => {
613                get => sub {
614                    my ($self) = @_;
615                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
616                        $self->base->get_object('address', $fmainaddress)
617                            ->_get_c_field($self->name);
618                    } else {
619                        return;
620                    }
621                },
622                set => $subsetaddress,
623            },
624            o => {
625                ro => 1,
626                iname => 'company',
627            },
628            ou => { iname => 'department', ro => 1 },
629            telephoneNumber => {
630                get => sub {
631                    my ($self) = @_;
632                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
633                        $self->base->get_object('address', $fmainaddress)
634                            ->_get_c_field($self->name);
635                    } else {
636                        return;
637                    }
638                },
639                set => $subsetaddress,
640            },
641            physicalDeliveryOfficeName => {
642                get => sub {
643                    my ($self) = @_;
644                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
645                        $self->base->get_object('address', $fmainaddress)
646                            ->_get_c_field($self->name);
647                    } else {
648                        return;
649                    }
650                },
651                set => $subsetaddress,
652            },
653            uid => { iname => 'name', ro => 1 },
654            cn =>  { iname => 'name', ro => 1 },
655            gecos => {
656                ro => 1,
657                get => sub {
658                    my ($self) = @_;
659                    my $obj = $self->object;
660                    my $gecos = sprintf("%s,%s,%s,%s",
661                        join(' ', grep { $_ }
662                                ($obj->_get_c_field('givenName'),
663                                ($obj->_get_c_field('sn'))))
664                            || $obj->_get_c_field('description') || '',
665                        join(' - ', grep { $_ } (($obj->_get_c_field('site') ||
666                                    $obj->_get_c_field('l')),
667                            $obj->_get_c_field('physicalDeliveryOfficeName'))) || '',
668                        $obj->_get_c_field('telephoneNumber') || '',
669                        $obj->_get_c_field('expireText') || '',
670                    );
671                    $gecos =~ s/:/ /g;
672                    return to_ascii($gecos);
673                },
674            },
675            displayName  => {
676                ro => 1, managed => 1,
677                get => sub {
678                    my ($self) = @_;
679                    return join(' ', grep { $_ } 
680                        (
681                            $self->object->_get_c_field('givenName'),
682                            $self->object->_get_c_field('sn')
683                        )
684                    )
685                    || $self->object->_get_c_field('description')
686                    || $self->object->id;
687                },
688            },
689            sAMAccountName  => {
690                ro => 1,
691                managed => 1,
692                iname => 'name',
693            },
694            accountExpires => {
695                ro => 1,
696                managed => 1,
697                get => sub {
698                    my ($self) = @_;
699                    my $obj = $self->object;
700                    my $sth = $obj->db->prepare_cached(
701                        sprintf(
702                            q{select extract(epoch from COALESCE(endcircuit,  expire)) + 11644474161 as expire
703                            from %s where %s = ?},
704                            $obj->db->quote_identifier($obj->_object_table),
705                            $obj->db->quote_identifier($obj->_key_field),
706                        )
707                    );
708                    $sth->execute($obj->id);
709                    my $res = $sth->fetchrow_hashref;
710                    $sth->finish;
711                    return $res->{expire} ? sprintf("%.f", $res->{expire} * 1E7) : '9223372036854775807';
712                }
713            },
714            shadowExpire => {
715                ro => 1,
716                managed => 1,
717                get => sub {
718                    my ($self) = @_;
719                    my $obj = $self->object;
720                    my $sth = $obj->db->prepare_cached(
721                        sprintf(
722                            q{select justify_hours(COALESCE(endcircuit,  expire) - '1/1/1970'::timestamp) as expire
723                            from %s where %s = ?},
724                            $obj->db->quote_identifier($obj->_object_table),
725                            $obj->db->quote_identifier($obj->_key_field),
726                        )
727                    );
728                    $sth->execute($obj->id);
729                    my $res = $sth->fetchrow_hashref;
730                    $sth->finish;
731                    return -1 unless($res->{expire});
732                    $res->{expire} =~ /(\d+) days\s*(\w)?/;
733                    return $1 + ($2 ? 1 : 0);
734                }
735            },
736            directReports => {
737                reference => 'user',
738                ro => 1,
739                delayed => 1,
740                get => sub {
741                    my ($self) = @_;
742                    my $obj = $self->object;
743                    my $sth = $obj->db->prepare_cached(
744                        q{
745                        SELECT
746                        "user".name FROM
747                        public."user",
748                        public.user_attributes_groups,
749                        public.group_attributes_users,
750                        public.group_attributes_base gb,
751                        public."group"
752                        WHERE
753                        "user".ikey = user_attributes_groups.okey AND
754                        user_attributes_groups.value = "group".name AND
755                        group_attributes_users.okey = gb.okey AND
756                        "group".ikey = group_attributes_users.okey AND
757                        gb.attr = 'sutype' AND
758                        gb.value = 'dpmt' AND
759                        group_attributes_users.attr = 'managedBy' AND
760                        group_attributes_users.value = ?
761                        } . ($self->base->{wexported} ? '' : ' and "user".exported = true') . q{
762                            and "user".ikey not in
763                        (select okey from user_attributes_users
764                        where attr = 'manager' and value != ? )
765                        union
766                        select "user".name FROM public."user",
767                        user_attributes_users where
768                        user_attributes_users.attr = 'manager' and user_attributes_users.value = ?
769                            and "user".ikey = user_attributes_users.okey
770                        } . ($self->base->{wexported} ? '' : ' and "user".exported = true')
771                    );
772                    $sth->execute($obj->id, $obj->id, $obj->id);
773                    my @res;
774                    while (my $res = $sth->fetchrow_hashref) {
775                        push(@res, $res->{name});
776                    }
777                    return \@res;
778                },
779            },
780            managedObjects => { ro => 1, reference => 'group', },
781            otheraddress => {
782                ro => 1,
783                reference => 'address',
784                get => sub {
785                    my ($self) = @_;
786                    my $sth = $self->base->db->prepare_cached(q{
787                        select name from address left join address_attributes
788                        on address.ikey = address_attributes.okey and
789                        address_attributes.attr = 'isMainAddress'
790                        where "user" = ?
791                        order by address_attributes.attr
792                        });
793                    $sth->execute($self->object->id);
794                    my @values;
795                    while (my $res = $sth->fetchrow_hashref) {
796                        push(@values, $res->{name});
797                    }
798                    return \@values;
799                },
800            },
801            mainaddress => {
802                ro => 1,
803                reference => 'address',
804                get => sub {
805                    my ($self) = @_;
806                    my $sth = $self->base->db->prepare_cached(q{
807                        select name from address join address_attributes on ikey = okey
808                        where "user" = ? and attr = 'isMainAddress'
809                        });
810                    $sth->execute($self->object->id);
811                    my $res = $sth->fetchrow_hashref;
812                    $sth->finish;
813                    return $res->{name};
814                },
815            },
816            postalAddress => {
817                ro => 1,
818                get => sub {
819                    my ($self) = @_;
820                    if (my $fmainaddress = $self->object->_get_c_field('mainaddress')) {
821                        $self->base->get_object('address', $fmainaddress)
822                            ->_get_c_field($self->name);
823                    } else {
824                        return;
825                    }
826                },
827            },
828            facsimileTelephoneNumber => { ro => 1, },
829            allsite   => {
830                ro => 1,
831                reference => 'site',
832            },
833            managerContact => {
834                delayed => 1,
835                can_values => sub {
836                    my %uniq = map { $_ => 1 } grep { $_ }
837                    ($_[1] ? $_[1]->get_attributes('managerContact') : ()),
838                    $base->search_objects('user', 'active=*');
839                    sort keys %uniq;
840                },
841                reference => 'user',
842                monitored => 1,
843                iname => 'manager',
844            },
845            expireText => {
846                ro => 1,
847                get => sub {
848                    my ($self) = @_;
849                    my $obj = $self->object;
850                    my $sth = $obj->db->prepare_cached(
851                        sprintf(
852                            q{select to_char(COALESCE(endcircuit,  expire), 'YYYY/MM/DD') as expire
853                            from %s where %s = ?},
854                            $obj->db->quote_identifier($obj->_object_table),
855                            $obj->db->quote_identifier($obj->_key_field),
856                        )
857                    );
858                    $sth->execute($obj->id) or $obj->db->rollback;
859                    my $res = $sth->fetchrow_hashref;
860                    $sth->finish;
861                    return $res->{expire}
862                },
863            },
864            krb5ValidEnd => {
865                ro => 1,
866                managed => 1,
867                get => sub {
868                    my ($self) = @_;
869                    my $sth = $self->object->db->prepare_cached(
870                        sprintf(
871                            q{select date_part('epoch', COALESCE(endcircuit,  expire))::int as expire
872                            from %s where %s = ?},
873                            $self->object->db->quote_identifier($self->object->_object_table),
874                            $self->object->db->quote_identifier($self->object->_key_field),
875                        )
876                    );
877                    $sth->execute($self->object->id) or $self->object->db->rollback;
878                    my $res = $sth->fetchrow_hashref;
879                    $sth->finish;
880                    return $res->{expire}
881                },
882            },
883            cells  => {
884                ro => 1,
885                reference => 'group',
886            },
887            departments => {
888                reference => 'group',
889                delayed => 1,
890                ro => 1,
891            },
892            arrivalDate => { },
893            expired => { ro => 1 },
894            active => { ro => 1 },
895            pwdAccountLockedTime => {
896                managed => 1,
897                ro => 1,
898                get => sub {
899                    my ($self) = @_;
900                    my $obj = $self->object;
901                    if ($obj->_get_c_field('locked')) {
902                        return '000001010000Z';
903                    } else {
904                        my $sth = $obj->db->prepare_cached(
905                            sprintf(
906                                q{select to_char(COALESCE(endcircuit,  expire) AT TIME ZONE 'Z', 'YYYYMMDDHH24MISSZ') as expire
907                                from %s where %s = ? and expire < now()},
908                                $obj->db->quote_identifier($obj->_object_table),
909                                $obj->db->quote_identifier($obj->_key_field),
910                            )
911                        );
912                        $sth->execute($obj->id);
913                        my $res = $sth->fetchrow_hashref;
914                        $sth->finish;
915                        return $res->{expire}
916                    }
917                },
918            },
919            userPassword => { readable => 0, },
920            wWWHomePage => { },
921            title => { },
922            snNative => { },
923            givenNameNative => { },
924            sn => { },
925            shadowWarning => { },
926            shadowMin => { },
927            shadowMax => { },
928            shadowLastChange => { },
929            shadowInactive => { },
930            shadowFlag => { },
931            otherTelephone => { },
932            nickname => { },
933            mobile => { },
934            mail => { },
935            labeledURI => { },
936            jobType => { },
937            ipPhone => { },
938            initials => { },
939            homePhone => { },
940            homeDirectory => { },
941            halReference => { },
942            grade => { },
943            givenName => { },
944            encryptedPassword => { },
945            description => { },
946            company => { },
947            comment => { },
948            college => { },
949            passwordLastSet => {
950                ro => 1,
951            },
952            currentEmployment => {
953                managed => 1,
954                ro => 1,
955                reference => 'employment',
956                get => sub {
957                    my ($attr) = @_;
958                    my $self = $attr->object;
959
960                    my $sth = $self->base->db->prepare_cached(
961                        q{
962                        select name from employment where firstday <= now() and
963                        (lastday is null or lastday >= now() - '1 days'::interval) and "user" = ?
964                        limit 1
965                        }
966                    );
967                    $sth->execute($self->id);
968                    my $res = $sth->fetchrow_hashref;
969                    $sth->finish;
970                    if ($res) {
971                        return $res->{name}
972                    } else {
973                        return;
974                    }
975                }
976            },
977            nextEmployment => {
978                managed => 1,
979                ro => 1,
980                reference => 'employment',
981                get => sub {
982                    my ($attr) = @_;
983                    my $self = $attr->object;
984
985                    my $sth = $self->base->db->prepare_cached(
986                        q{
987                        select name from employment where firstday > now() and
988                        (lastday is null or lastday >= now() - '1 days'::interval) and "user" = ?
989                        limit 1
990                        }
991                    );
992                    $sth->execute($self->id);
993                    my $res = $sth->fetchrow_hashref;
994                    $sth->finish;
995                    if ($res) {
996                        return $res->{name}
997                    } else {
998                        return;
999                    }
1000                }
1001            },
1002            appliedEmployement => {
1003                hidden => 1,
1004                reference => 'employment',
1005            },
1006    };
1007
1008    foreach (qw(expire contratType managerContact company endcircuit department)) {
1009        $attrs->{$_}{ro} = sub {
1010
1011            my $setting = $base->config('employment_lock_user') || 'any';
1012
1013            for ($setting) {
1014                /^always$/ and return 1;
1015                /^never$/ and return 0;
1016
1017                !$_[0] and return 1;
1018
1019                /^any$/i and return $_[0]->listEmployment ? 1 : 0;
1020                /^active/i and do {
1021                    return $_[0]->_get_c_field('currentEmployment')
1022                        ? 1
1023                        : $_[0]->_get_c_field('nextEmployment') ? 1 : 0;
1024                };
1025                /(\S+)=(\S+)/ and do {
1026                    my $attr = $_[0]->_get_c_field($1);
1027                    if (defined($attr)) {
1028                        if ($2 eq '*') {
1029                            return 1;
1030                        } elsif($2 eq $attr) {
1031                            return 1;
1032                        } else {
1033                            return 0;
1034                        }
1035                    } else {
1036                        return 0;
1037                    }
1038                };
1039            }
1040            return $_[0]->listEmployment ? 1 : 0; # default is any!
1041        };
1042    }
1043
1044    $class->SUPER::_get_attr_schema($base, $attrs)
1045}
1046
1047sub _get_state {
1048    my ($self, $state) = @_;
1049    for ($state) {
1050        /^expired$/ and do {
1051            my $attribute = $self->attribute('expire');
1052            $attribute->check_acl('r') or return;
1053            my $sth = $self->db->prepare_cached(
1054                q{ select coalesce(expire < now(), false) as exp from "user"
1055                where "user".name = ?}
1056            );
1057            $sth->execute($self->id);
1058            my $res = $sth->fetchrow_hashref;
1059            $sth->finish;
1060            return $res->{exp} ? 1 : 0;
1061        };
1062    }
1063}
1064
1065=head2 listEmployment
1066
1067Return the ordered list of contract
1068
1069=cut
1070
1071sub listEmployment {
1072    my ($self) = @_;
1073
1074    my $sth = $self->base->db->prepare_cached(
1075        q{
1076        select name from employment where "user" = ?
1077        order by lastday desc NULLS first
1078        }
1079    );
1080    $sth->execute($self->id);
1081    my @list = ();
1082    while (my $res = $sth->fetchrow_hashref) {
1083        push(@list, $res->{name});
1084    }
1085
1086    @list
1087}
1088
1089
1090sub computeEndEmployment {
1091    my ($self, $delay) = @_;
1092
1093    my $list_empl = $self->base->db->prepare_cached(q{
1094        SELECT *, firstday <= now() as "started" FROM employment WHERE "user" = ? and
1095        (lastday is null or lastday >= now() - '1 days'::interval)
1096        order by firstday asc
1097        });
1098    $list_empl->execute($self->id);
1099    my $end;
1100    while (my $res = $list_empl->fetchrow_hashref) {
1101        if (!$res->{lastday}) {
1102            # Ultimate employment.
1103            $list_empl->finish;
1104            return undef;
1105        }
1106        if ($end) {
1107            my $nextstart = DateTime->from_epoch(epoch => str2time($res->{firstday}));
1108            my $tend = $end->clone;
1109            if ($delay) {
1110                $tend->add(days => $delay);
1111            }
1112            if ($tend->ymd lt $nextstart->ymd) {
1113                last;
1114            }
1115        } else {
1116            if (!$res->{started}) {
1117                last;
1118            }
1119        }
1120        $end = DateTime->from_epoch(epoch => str2time($res->{lastday}));
1121        $end->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
1122        $end->add(hours => 23, minutes => 59, seconds => 59);
1123    }
1124    $list_empl->finish;
1125
1126    if (!$end) {
1127        my $listold = $self->base->db->prepare_cached(q{
1128            SELECT max(lastday) as lastday FROM employment WHERE "user" = ? and
1129            lastday IS NOT NULL and lastday <= now() - '1 days'::interval
1130            });
1131        $listold->execute($self->id);
1132        my $res = $listold->fetchrow_hashref;
1133        if ($res && $res->{lastday}) {
1134            $end = DateTime->from_epoch(epoch => str2time($res->{lastday}));
1135            $end->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
1136            $end->add(hours => 23, minutes => 59, seconds => 59);
1137        } else {
1138            if (my $default = $self->base->config('unemployed_expire')) {
1139                $end = DateTime->from_epoch(epoch => str2time($default));
1140            }
1141        }
1142        $listold->finish;
1143    }
1144    return $end ? $end->iso8601 : undef
1145}
1146
11471;
1148
1149__END__
1150
1151=head1 SEE ALSO
1152
1153=head1 AUTHOR
1154
1155Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
1156
1157=head1 COPYRIGHT AND LICENSE
1158
1159Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
1160
1161This library is free software; you can redistribute it and/or modify
1162it under the same terms as Perl itself, either Perl version 5.10.0 or,
1163at your option, any later version of Perl 5 you may have available.
1164
1165=cut
Note: See TracBrowser for help on using the repository browser.