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

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