source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases.pm @ 1855

Last change on this file since 1855 was 1839, checked in by nanardon, 8 years ago

Fix value test on undef list

  • Property svn:keywords set to Id Rev
File size: 28.0 KB
Line 
1package LATMOS::Accounts::Bases;
2
3use 5.010000;
4use strict;
5use warnings;
6use LATMOS::Accounts::Bases::Objects;
7use LATMOS::Accounts::Bases::Attributes;
8use LATMOS::Accounts::Log;
9use LATMOS::Accounts::Utils qw(exec_command to_ascii);
10
11our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
12
13=head1 NAME
14
15LATMOS::Accounts::Bases - Base class for account data bases
16
17=head1 SYNOPSIS
18
19  use LATMOS::Accounts::Bases;
20  my $base = LATMOS::Accounts::Bases->new('type', %options);
21  ...
22
23=head1 DESCRIPTION
24
25This module provide basic functions for various account base
26
27=head1 FUNCTIONS
28
29=cut
30
31=head2 new($type, %options)
32
33Return, if success, a new data base account object, $type is
34account base type, %options to setup the base.
35
36=cut
37
38sub new {
39    my ($class, $type, $options) = @_;
40
41    my $pclass = ucfirst(lc($type));
42    eval "require LATMOS::Accounts::Bases::$pclass;";
43    if ($@) {
44        la_log(LA_DEBUG, "Failed to load base type `%s': %s", $type, $@);
45        return
46    }
47    my $base = "LATMOS::Accounts::Bases::$pclass"->new(%{$options->{params}})
48        or return;
49    $base->{_type} = lc($pclass);
50    $base->{_label} = $options->{label};
51    $base->{_options} = $options->{params};
52    $base->{wexported} = 0;
53    $base->{defattr} = $options->{defattr};
54    $base->{_acls} = $options->{acls};
55    $base->{_allowed_values} = $options->{allowed_values};
56    $base->{_la} = $options->{la};
57    la_log(LA_DEBUG, 'Instanciate base %s (%s)', ($base->label || 'N/A'), $pclass);
58    $base
59}
60
61=head2 load
62
63Make account base loading data into memory if need.
64Should always be called, if database fetch data on the fly
65(SQL, LDAP), the function just return True.
66
67=cut
68
69# override by the database driver if need
70sub load { 1 }
71
72=head2 label
73
74Return the database label
75
76=cut
77
78sub label {
79    $_[0]->{_label} || 'NoLabel';
80}
81
82=head2 type
83
84Return the type of the base
85
86=cut
87
88sub type {
89    $_[0]->{_type};
90}
91
92=head2 la
93
94return LATMOS::Accounts object parent to the base
95
96=cut
97
98sub la { $_[0]->{_la} };
99
100=head2 config ($opt)
101
102Return options from config
103
104=cut
105
106sub config {
107    my ($self, $opt) = @_;
108    return $self->{_options}{$opt};
109}
110
111=head2 wexported
112
113See L</unexported>
114
115=cut
116
117sub wexported { unexported(@_) }
118
119=head2 unexported ($wexported)
120
121Set base to report unexported object or not
122
123=cut
124
125sub unexported {
126    my ($self, $wexported) = @_;
127    my $old = $self->{wexported};
128    if (defined($wexported)) {
129        $self->{wexported} = $wexported;
130        $self->log(LA_DEBUG, "Switching exported mode: %s => %s", $old,
131            $wexported);
132    }
133    return($old || 0);
134}
135
136=head2 temp_switch_unexported($CODE, $value)
137
138Switch the base to unexported mode given by C<$value>, run C<$CODE>, restore
139back the previous state and return the result of code ref.
140
141=cut
142
143sub temp_switch_unexported (&;$) {
144    my ($self, $sub, $value) = @_;
145
146    my $old = $self->unexported($value || 0);
147    my $res = $sub->();
148    $self->unexported($old);
149    return $res;
150}
151
152=head2 log($level, $msg, $arg)
153
154Log a message prefixed by database information
155
156=cut
157
158sub log {
159    my ($self, $level, $msg, @args) = @_;
160    my $prefix = 'Base(' . $self->type . '/' . $self->label . ')';
161    LATMOS::Accounts::Log::la_log($level, "$prefix $msg", @args);
162}
163
164=head2 ReportChange($otype, $name, $ref, $changetype, $message, @args)
165
166Functions to report back
167
168=cut 
169
170sub ReportChange {}
171
172=head2 list_supported_objects(@otype)
173
174Return a list of supported object
175
176@type is an additionnal list of objects to check
177
178=cut
179
180sub list_supported_objects {
181    my ($self, @otype) = @_;
182    my %res;
183    foreach my $inc (@INC) {
184        my $sub = 'LATMOS::Accounts::Bases::' . ucfirst($self->type);
185        $sub =~ s/::/\//g;
186        foreach (glob("$inc/$sub/[A-Z]*.pm")) {
187            s/.*\///;
188            s/\.pm$//;
189            $res{lc($_)} = 1;
190        }
191    }
192    $res{$_} = 1 foreach(@otype);
193    my @sobj = grep { $self->is_supported_object($_) } keys %res;
194    la_log(LA_DEBUG, "Base %s supported objects: %s", $self->type, join(', ', @sobj));
195    return @sobj;
196}
197
198=head2 ordered_objects
199
200Return supported object type ordered in best order for synchronisation
201
202=cut
203
204sub ordered_objects {
205    my ($self) = @_;
206
207    my %deps;
208    my %maxdeps;
209    my @objs = sort { $b cmp $a } $self->list_supported_objects;
210    foreach my $obj (@objs) {
211        foreach my $at ($self->list_canonical_fields($obj)) {
212            my $attr = $self->attribute($obj, $at);
213            $attr->ro and next;
214            $attr->{delayed} and next;
215            if (my $res = $attr->reference) {
216                $deps{$obj}{$res} ||= 1;
217                if ($attr->mandatory) {
218                    $deps{$obj}{$res} = 2;
219                    $maxdeps{$res} = 1;
220                }
221            }
222        }
223    }
224
225    sort {
226        if (keys %{$deps{$a} || {}}) {
227            if (keys %{$deps{$b} || {}}) {
228                return (
229                    ($deps{$a}{$b} || 0) > ($deps{$b}{$a} || 0) ?  1 :
230                    ($deps{$b}{$a} || 0) > ($deps{$a}{$b} || 0) ? -1 :
231                    ($maxdeps{$b} || 0) - ($maxdeps{$a} || 0)
232                );
233            } else {
234                return 1;
235            }
236        } elsif (keys %{$deps{$b} || {}}) {
237            return -1;
238        } else {
239            return  ($maxdeps{$b} || 0) - ($maxdeps{$a} || 0)
240        }
241    } @objs;
242}
243
244sub _load_obj_class {
245    my ($self, $otype) = @_;
246
247    $otype or die "No type given: " . join(', ', caller) . "\n";
248    # finding perl class:
249    my $pclass = ref $self;
250    $pclass .= '::' . ucfirst(lc($otype));
251    eval "require $pclass;";
252    if ($@) {
253        $self->log(LA_DEBUG, 'Cannot load perl class %s', $pclass);
254        return
255    } # error message ?
256    return $pclass;
257}
258
259
260=head2 is_supported_object($otype)
261
262Return true is object type $otype is supported
263
264=cut
265
266sub is_supported_object {
267    my ($self, $otype) = @_;
268   
269    if (my $pclass = $self->_load_obj_class($otype)) {
270        if ($pclass->can('is_supported')) {
271            return $pclass->is_supported($self);
272        } else {
273            return 1;
274        }
275    } else {
276        return 0;
277    }
278}
279
280=head2 list_objects($otype)
281
282Return the list of UID for object of $otype.
283
284=cut
285
286sub list_objects {
287    my ($self, $otype) = @_;
288    my $pclass = $self->_load_obj_class($otype) or return;
289    $pclass->list($self);
290}
291
292=head2 get_object($type, $id)
293
294Return an object of $type (typically user or group) having identifier
295$id.
296
297=cut
298
299sub get_object {
300    my ($self, $otype, $id) = @_;
301
302    return LATMOS::Accounts::Bases::Objects->_new($self, $otype, $id);
303}
304
305=head2 create_c_object($type, $id, %data)
306
307Create and return an object of type $type with unique id
308$id having %data using canonical fields
309
310=cut
311
312sub create_c_object {
313    my ($self, $otype, $id, %cdata) = @_;
314    $self->check_acl($otype, '@CREATE', 'w') or do {
315        $self->log(LA_WARN, 'permission denied to create object type %s',
316            $otype);
317        return;
318    };
319
320    $self->_create_c_object($otype, $id, %cdata);
321}
322
323sub _create_c_object {
324    my ($self, $otype, $id, %cdata) = @_;
325
326    $id ||= ''; # Avoid undef
327
328    if (my $chk = (
329        lc($otype) eq 'user' || lc($otype) eq 'group')
330        ? LATMOS::Accounts::Utils::check_ug_validity($id)
331        : LATMOS::Accounts::Utils::check_oid_validity($id)) {
332        $self->log(LA_ERR, "Cannot create $otype with ID $id `%s:'", $chk);
333        return;
334    }
335    foreach my $cfield (keys %cdata) {
336        $self->check_allowed_values($otype, $cfield, $cdata{$cfield}) or do {
337            my $last = LATMOS::Accounts::Log::lastmessage(LA_ERR);
338            $self->log(LA_ERR, "Cannot create $otype, wrong value%s", ($last ? ": $last" : ''));
339            return;
340        };
341    }
342
343    # populating default value
344    {
345        my %default = $self->compute_default($otype, $id, %cdata);
346        foreach my $k (keys %default) {
347            $cdata{$k} = $default{$k} unless(exists($cdata{$k}));
348        }
349    }
350
351    my %data;
352    foreach my $cfield (keys %cdata) {
353        my $attribute = $self->attribute($otype, $cfield) or next;
354        $attribute->ro and next;
355        $data{$attribute->iname} = $cdata{$cfield};
356    }
357    $self->create_object($otype, $id, %data) or return;
358    my $obj = $self->get_object($otype, $id) or return;
359    $obj->ReportChange('Create', 'Object created with %s', join(', ', sort keys %cdata));
360
361    foreach my $attrname (keys %data) {
362        my $attribute = $self->attribute($obj->type, $attrname) or next;
363        $attribute->monitored or next;
364
365        $obj->ReportChange('Attributes', '%s set to %s', $attrname,
366            (ref $data{$attrname}
367                ? join(', ', @{ $data{$attrname} })
368                : $data{$attrname}) || '(none)');
369    }
370
371    $obj
372}
373
374=head2 create_object($type, $id, %data)
375
376Create and return an object of type $type with unique id
377$id having %data.
378
379This method should be provided by the data base handler.
380
381=cut
382
383sub create_object {
384    my ($self, $otype, $id, %data) = @_;
385    "$id" or do {
386        $self->log(LA_ERR, l("Cannot create %s object with empty id"),
387            $otype);
388        return;
389    };
390    my $pclass = $self->_load_obj_class($otype) or do {
391        $self->log(LA_ERR, "Cannot create %s object type (cannot load class)",
392            $otype);
393        return;
394    };
395
396    if (!$pclass->checkValues($self, $id, %data)) {
397        my $last = LATMOS::Accounts::Log::lastmessage(LA_ERR);
398        la_log(LA_ERR,
399            'Cannot create %s (%s) in base %s (%s): wrong value%s',
400            $id, $otype, $self->label, $self->type,
401            ($last ? ": $last" : ''),
402        );
403        return;
404    }
405
406    if ($pclass->_create($self, $id, %data)) {
407        la_log(LA_INFO,
408            'Object %s (%s) created in base %s (%s)',
409            $id, $otype, $self->label, $self->type,
410        );
411    } else {
412        my $last = LATMOS::Accounts::Log::lastmessage(LA_ERR);
413        warn $last;
414        la_log(LA_ERR,
415            'Object creation %s (%s) in base %s (%s) failed%s',
416            $id, $otype, $self->label, $self->type,
417            ($last ? ": $last" : ''),
418        );
419        return;
420    };
421
422    $self->get_object($otype, $id);
423}
424
425=head2 defaultAttributeValue($otype, $attr)
426
427Return default static value for attribute C<$attr>
428
429=cut
430
431sub defaultAttributeValue {
432    my ($self, $otype, $attr) = @_;
433
434    my %def = %{ $self->{defattr} || {}};
435    return $def{"$otype.$attr"} || '';
436}
437
438=head2 compute_default($otype, $id, %cdata)
439
440Return a hash containing value to set for new object
441
442=cut
443
444sub compute_default {
445    my ($self, $otype, $id, %cdata) = @_;
446
447    my %default;
448    foreach my $def (keys %{ $self->{defattr} || {}}) {
449        if ($def =~ /^$otype\.(.*)$/) {
450            $default{$1} = $self->{defattr}{$def} if(!$cdata{$1});
451        }
452    }
453
454    # computed default value (not a simple set)
455    if (lc($otype) eq 'user') {
456        if (!$cdata{homeDirectory}) {
457            $default{homeDirectory} = $self->{defattr}{'user.homebase'}
458                ? $self->{defattr}{'user.homebase'} . "/$id" 
459                : '';
460        }
461
462        if (!$cdata{uidNumber}) {
463            $default{uidNumber} ||= $self->find_next_numeric_id('user', 'uidNumber',
464            $self->{defattr}{'user.min_uid'}, $self->{defattr}{'user.max_uid'});
465        }
466
467        my $mailid = $cdata{givenName} && $cdata{sn}
468            ? sprintf('%s.%s',
469                to_ascii(lc($cdata{givenName})),
470                to_ascii(lc($cdata{sn})),)
471            : undef;
472        $mailid =~ s/\s+/-/g if($mailid);
473
474        if ($mailid &&
475            $self->is_supported_object('aliases') &&
476            ! $self->get_object('aliases', $mailid)) {
477            if (my $attr = $self->attribute($otype, 'mail')) {
478                if ((!$attr->ro) && $self->{defattr}{'user.maildomain'}) {
479                    $default{mail} ||= sprintf('%s@%s',
480                    $mailid,
481                    $self->{defattr}{'user.maildomain'});
482                }
483            }
484            if (my $attr = $self->attribute($otype, 'aliases')) {
485                $default{aliases} ||= $mailid unless ($attr->ro);
486            }
487            if (my $attr = $self->attribute($otype, 'revaliases')) {
488                $default{revaliases} ||= $mailid unless ($attr->ro);
489            }
490        }
491    } elsif (lc($otype) eq 'group') {
492        if (!$cdata{gidNumber}) {
493            $default{gidNumber} ||= $self->find_next_numeric_id(
494                'group', 'gidNumber',
495                $self->{defattr}{'group.min_gid'},
496                $self->{defattr}{'group.max_gid'}
497            );
498        }
499    }
500
501    return %default;
502}
503
504sub _allowed_values {
505    $_[0]->{_allowed_values}
506}
507
508=head2 obj_attr_allowed_values ($otype, $attr)
509
510Return value allowed for this attribute
511
512=cut
513
514sub obj_attr_allowed_values {
515    my ($self, $otype, $attr) = @_;
516    if ($self->_allowed_values &&
517        $self->_allowed_values->SectionExists("$otype.$attr")) {
518        return grep { defined($_) } $self->_allowed_values->val("$otype.$attr", 'allowed');
519    }
520    return();
521}
522
523=head2 check_allowed_values ($otype, $attr, $attrvalues)
524
525Check attributes C<$attr> of object type C<$otype> allow values C<$attrvalues>
526
527=cut
528
529sub check_allowed_values {
530    my ($self, $otype, $attr, $attrvalues) = @_;
531    $self->_allowed_values or return 1;
532    my @values = ref $attrvalues ? @{ $attrvalues } : $attrvalues;
533    foreach my $value (@values) {
534        $value or next;
535        if (my @allowed = $self->obj_attr_allowed_values($otype, $attr)) {
536            grep { $value eq $_ } @allowed or do {
537                $self->log(LA_ERR,
538                    "value `%s' is not allow for %s.%s per configuration (allowed_values)",
539                    $value, $otype, $attr
540                );
541                return;
542            };
543        }
544    }
545    return 1;
546}
547
548=head2 list_canonical_fields($otype, $for)
549
550Return the list of supported fields by the database for object type $otype.
551
552Optionnal $for specify the goal for which the list is requested, only supported
553fields will be returns
554
555=cut
556
557sub list_canonical_fields {
558    my ($self, $otype, $for) = @_;
559    $for ||= 'rw';
560    my $pclass = $self->_load_obj_class($otype) or return;
561    sort $pclass->_canonical_fields($self, $for);
562}
563
564sub _get_attr_schema {
565    my ($self, $otype) = @_;
566    my $pclass = $self->_load_obj_class($otype) or return;
567    return $pclass->_get_attr_schema($self);
568}
569
570=head2 get_attr_schema
571
572Deprecated
573
574=cut
575
576# TODO: kill this
577
578sub get_attr_schema {
579    my ($self, $otype, $attribute) = @_;
580    my $info = $self->_get_attr_schema($otype);
581    if ($info->{$attribute}) {
582        return $info->{$attribute};
583    } else {
584        return;
585    }
586}
587
588=head2 attribute($otype, $attribute)
589
590Return attribute object.
591
592See L<LATMOS::Accounts::Bases::Attribute>
593
594=cut
595
596sub attribute {
597    my ($self, $otype, $attribute) = @_;
598
599    my $attrinfo;
600    if (!ref $attribute) {
601       $attrinfo = $self->get_attr_schema($otype, $attribute)
602            or return;
603        $attrinfo->{name} = $attribute;
604    } else {
605        $attrinfo = $attribute;
606    }
607
608    return LATMOS::Accounts::Bases::Attributes->new(
609        $attrinfo,
610        $self,
611        $otype,
612    );
613}
614
615=head2 delayed_fields
616
617DEPRECATED
618
619=cut 
620
621# TODO: kill this
622
623sub delayed_fields {
624    my ($self, $otype, $for) = @_;
625    $self->log(LA_WARN, "calling DEPRECATED delayed_fields " . join(',',
626            caller));
627    $for ||= 'rw';
628    my @attrs;
629    foreach ($self->list_canonical_fields($otype, $for)) {
630        my $attr = $self->attribute($otype, $_) or next;
631        $for =~ /w/ && $attr->ro and next;
632        $attr->delayed or next;
633        push(@attrs, $_);
634    }
635    @attrs
636}
637
638=head2 ochelper ($otype)
639
640Return L<LATMOS::Accounts::Bases::OChelper> object
641
642=cut
643
644sub ochelper {
645    my ($self, $otype) = @_;
646    my $pclass = ucfirst(lc($otype));
647    foreach my $class (
648        ref($self) . '::OCHelper::' . $pclass,
649        ref($self) . '::OCHelper',
650        "LATMOS::Accounts::Bases::OCHelper::$pclass",
651        'LATMOS::Accounts::Bases::OCHelper' ) {
652        eval "require $class;";
653        if ($@) { next } # error message ?
654        my $ochelper = "$class"->new($self, $otype);
655        return $ochelper;
656    }
657    return;
658}
659
660=head2 delete_object($otype, $id)
661
662Destroy from data base object type $otype having id $id.
663
664=cut
665
666sub delete_object {
667    my ($self, $otype, $id) = @_;
668    my $obj = $self->get_object($otype, $id) or do {
669        $self->log(LA_WARN, 'Cannot delete %s/%s: no such object',
670            $otype, $id);
671        return;
672    };
673    $self->check_acl($obj, '@DELETE', 'w') or do {
674        $self->log(LA_WARN, 'permission denied to delete %s/%s',
675            $otype, $id);
676        return;
677    };
678    my $ref = $obj->Iid;
679    if (my $res = $self->_delete_object($otype, $id)) {
680        $self->ReportChange($otype, $id, $ref, 'Delete', 'Object deleted');
681        return $res;
682    }
683    return;
684}
685
686sub _delete_object {
687    my ($self, $otype, $id) = @_;
688    my $pclass = $self->_load_obj_class($otype);
689    $pclass->_delete($self, $id);
690}
691
692=head2 rename_object($otype, $id, $newid)
693
694Rename an object.
695
696=cut
697
698sub rename_object {
699    my ($self, $otype, $id, $newid) = @_;
700
701    $self->log(LA_DEBUG, "Trying to rename $otype/$id to $newid");
702    my $obj = $self->get_object($otype, $id) or do {
703        $self->log(LA_WARN, 'Cannot rename %s/%s: no such object',
704            $otype, $id);
705        return;
706    };
707    if (my $chk = (lc($otype) eq 'user' || lc($otype) eq 'group')
708        ? LATMOS::Accounts::Utils::check_ug_validity($newid)
709        : LATMOS::Accounts::Utils::check_oid_validity($newid)) {
710        $self->log(LA_ERR, "Cannot rename $otype/$id to ID $newid `%s:'", $chk);
711        return;
712    }
713    $self->check_acl($obj, '@DELETE', 'w') &&
714    $self->check_acl($obj, '@CREATE', 'w') or do {
715        $self->log(LA_WARN, 'permission denied to rename %s/%s',
716            $otype, $id);
717        return;
718    };
719
720    my $oldref = $obj->Iid;
721
722    if (my $res = $self->_rename_object($otype, $id, $newid)) {
723        my $newobj = $self->get_object($otype, $newid) or do {
724            $self->log(LA_WARN, 'Cannot get object %s/%s: rename failed ?',
725                $otype, $id);
726            return;
727        };
728
729        $self->ReportChange($otype, $id, $oldref, 'Rename', 'Object rename to %s', $newid);
730        $newobj->ReportChange('Rename', 'Object renamed from %s', $id);
731        return $res;
732    }
733    return;
734}
735
736sub _rename_object {
737    my ($self, $otype, $id, $newid) = @_;
738    my $pclass = $self->_load_obj_class($otype);
739    $pclass->can('_rename') or do {
740        $self->log(LA_ERR, 'rename object type %s is unsupported', $otype);
741        return;
742    };
743    $pclass->_rename($self, $id, $newid);
744}
745
746=head2 is_transactionnal
747
748Return True is the database support commit and rollback
749
750=cut
751
752sub is_transactionnal {
753    my ($self) = @_;
754    return($self->can('_rollback') && $self->can('_commit'));
755}
756
757=head2 commit
758
759Save change into the database if change are not done immediately.
760This should always be called as you don't know when change are applied.
761
762Return always true if database does not support any transaction.
763
764The driver should provides a _commit functions to save data.
765
766=cut
767
768sub commit {
769    my ($self) = @_;
770    if ($self->can('_commit')) {
771        la_log(LA_DEBUG, 'Commiting data');
772        if (!(my $res = $self->_commit)) {
773            la_log(LA_ERR, "Commit error on %s", $_->label);
774            return $res;
775        }
776    }
777
778    $self->postcommit();
779
780    return 1;
781}
782
783=head2 postcommit
784
785Run postcommit command
786
787=cut
788
789sub postcommit {
790    my ($self) = @_;
791
792    if ($self->{_options}{postcommit}) {
793        exec_command($self->{_options}{postcommit},
794            {
795                BASE => $self->label,
796                BASETYPE => $self->type,
797                HOOK_TYPE => 'POST',
798                CONFIG => $self->{_options}{configdir}, 
799            }
800        );
801    } else {
802        return 1;
803    }
804}
805
806=head2 rollback
807
808If database support transaction, rollback changes. Return false
809if database does not support.
810
811If supported, driver should provides a _rollback functions
812
813=cut
814
815sub rollback {
816    my ($self) = @_;
817    if ($self->can('_rollback')) {
818       la_log(LA_DEBUG, 'Rolling back data');
819       return $self->_rollback;
820   } else {
821       return 0;
822   }
823}
824
825=head2 current_rev
826
827Return the current revision of the database
828
829Must be provide by base driver if incremental synchro is supported
830
831=cut
832
833sub current_rev { return }
834
835=head2 list_objects_from_rev($otype, $rev)
836
837Return the list of UID for object of $otype.
838
839=cut
840
841sub list_objects_from_rev {
842    my ($self, $otype, $rev) = @_;
843    my $pclass = $self->_load_obj_class($otype) or return;
844    if (defined($rev) && $pclass->can('list_from_rev')) {
845        return $pclass->list_from_rev($self, $rev);
846    } else {
847        # no support, return all objects...
848        return $self->list_objects($otype);
849    }
850}
851
852=head2 sync_object_from($srcbase, $otype, $id, %options)
853
854Sync object type C<$otype> C<$id> from base C<$srcbase> to current base.
855
856C<%options>:
857
858=over 4
859
860=item nodelete
861
862Don't delete object if the object synchronize don't exist in source base
863
864=back
865
866=cut
867
868sub sync_object_from {
869    my ($self, $srcbase, $otype, $id, %options) = @_;
870
871    # is the object type supported by both
872    foreach ($self, $srcbase) {
873        $_->is_supported_object($otype) or return '';
874    }
875   
876    if (my $srcobj = $srcbase->get_object($otype, $id)) {
877        return $self->sync_object($srcobj, %options);
878    } elsif (!$options{nodelete}) {
879        $self->_delete_object($otype, $id) and return 'DELETED';
880    }
881    return;
882}
883
884=head2 sync_object
885
886Synchronise an object into this base
887
888=cut
889
890sub sync_object {
891    my ($self, $srcobj, %options) = @_;
892    $self->is_supported_object($srcobj->type) or return '';
893    my @fields = $options{attrs}
894        ? @{ $options{attrs} }
895        : $self->list_canonical_fields($srcobj->type, 'w');
896    my %data;
897    foreach (@fields) {
898        # check attribute exists in source:
899        my $attr = $srcobj->attribute($_) or next;
900        $attr->readable or next;
901        if (! $options{onepass}) {
902            if ($options{firstpass}) {
903                $attr->delayed and next;
904            } else {
905                $attr->delayed or next;
906            }
907        }
908        $data{$_} = $srcobj->_get_c_field($_);
909    }
910    if (my $dstobj = $self->get_object($srcobj->type, $srcobj->id)) {
911        keys %data or return 'SYNCED';
912        foreach (keys %data) {
913            if (!$dstobj->attribute($_) ||
914                $dstobj->attribute($_)->ro) {
915                delete($data{$_});
916            }
917        }
918        my $res = $dstobj->_set_c_fields(%data);
919        if (defined $res) {
920            return $res ? 'SYNCED' : '';
921        } else {
922            return;
923        }
924    } elsif(!$options{nocreate}) {
925        if ((! $options{firstpass}) && (!$options{onepass})) {
926            $self->log(LA_ERR, 'This is not first pass, creation wanted but denied');
927            return;
928        }
929        if (my $res = $self->_create_c_object($srcobj->type, $srcobj->id, %data)) {
930            return 'CREATED'
931        } else {
932            return;
933        }
934    } else {
935        # No error, but creation is denied
936        return 'Creation skipped';
937    }
938
939    return;
940}
941
942=head2 search_objects($otype, @filter)
943
944Search object according @filter. @filter is a list
945of field/value which should match.
946
947A default function is provided but each db driver can provide
948an optimize version.
949
950=cut
951
952sub search_objects {
953    my ($self, $otype, @filter) = @_;
954    my $pclass = $self->_load_obj_class($otype) or return;
955    $pclass->search($self, @filter);
956}
957
958=head2 attributes_summary($otype, $attr)
959
960Return couple object id / value for attribute C<$attr> of object type C<$otype>
961
962This method is designed to be faster than fetching object one by one.
963
964=cut
965
966sub attributes_summary {
967    my ($self, $otype, $attr) = @_;
968    my $pclass = $self->_load_obj_class($otype) or return;
969    $pclass->attributes_summary($self, $attr);
970}
971
972=head2 attributes_summary_by_object($otype, $attr)
973
974Return couple object id / value for attribute C<$attr> of object type C<$otype>
975
976This method is designed to be faster than fetching object one by one.
977
978=cut
979
980sub attributes_summary_by_object {
981    my ($self, $otype, $attr) = @_;
982    my $pclass = $self->_load_obj_class($otype) or return;
983    $pclass->attributes_summary_by_object($self, $attr);
984}
985
986=head2 fetchObjectInfo($otype, $attributes, @filters)
987
988Return an hashref with attribute data for each object matching @filters.
989
990If C<$attributes> can be an array ref to a list of attributes.
991
992=cut
993
994sub fetchObjectInfo {
995    my ($self, $otype, $attributes, @filters) = @_;
996
997    my %results = map { $_ => {} }  $self->search_objects($otype, @filters);
998
999    foreach my $attr ( ref $attributes ? @$attributes : $attributes) {
1000        my %res = $self->attributes_summary_by_object($otype, $attr);
1001        foreach my $obj (keys %results) {
1002            $results{$obj}{$attr} = $res{$obj};
1003        }
1004    }
1005
1006    \%results;
1007}
1008
1009=head2 find_next_numeric_id($otype, $field, $min, $max)
1010
1011Return, if possible, next numeric id available (typically unix user UID).
1012
1013=cut
1014
1015sub find_next_numeric_id {
1016    my ($self, $otype, $field, $min, $max) = @_;
1017    my $pclass = $self->_load_obj_class($otype) or return;
1018    $pclass->find_next_numeric_id($self, $field, $min, $max);
1019}
1020
1021=head2 authenticate_user($username, $passwd)
1022
1023Return true if authentication success.
1024
1025Must be override by driver if the base have a proper authentication method
1026
1027=cut
1028
1029sub authenticate_user {
1030    my ($self, $username, $passwd) = @_;
1031    $username or return;
1032    my $uobj = $self->get_object('user', $username) or do {
1033        la_log(LA_ERR, "Cannot authenticate non existing user $username");
1034        return;
1035    };
1036
1037    if ($uobj->_get_c_field('expired')) {
1038        la_log(LA_ERR, "Account $username has expired (%s)",
1039            $uobj->_get_c_field('expired'));
1040        return;
1041    }
1042
1043    if ($uobj->_get_c_field('locked')) {
1044        la_log(LA_ERR, "Account $username is currently locked");
1045        return;
1046    }
1047
1048    my $password = $uobj->get_field('userPassword') or do {
1049        la_log(LA_ERR, "Cannot authenticate user $username having no passwd");
1050        return;
1051    };
1052    if ($password eq crypt($passwd, $password)) { # crypt unix
1053        la_log(LA_NOTICE, "User $username authenticated");
1054        return 1;
1055    } else {
1056        la_log(LA_ERR, "Cannot authenticate user $username");
1057        return 0;
1058    }
1059}
1060
1061=head2 connect($username, $password)
1062
1063Authenticate the user and store the username as connected
1064
1065=cut
1066
1067sub connect {
1068    my ($self, $username, $password) = @_;
1069    my $auth = $self->authenticate_user($username, $password);
1070    if ($auth) {
1071        $self->{_user} = $username;
1072        la_log(LA_DEBUG, "Connected as $username");
1073    }
1074    return $auth;
1075}
1076
1077=head2 user
1078
1079Return the current connected username
1080
1081=cut
1082
1083sub user { $_[0]->{_user} }
1084
1085=head2 check_acl($obj, $attr, $perm)
1086
1087Return true if connected user have C<$perm> permission on attribute C<$attr> of
1088object C<$obj>.
1089
1090=cut
1091
1092sub check_acl {
1093    my ($self, $obj, $attr, $perm) = @_;
1094    if ($self->{_acls}) {
1095        my ($who, $groups) = ($self->user || '');
1096
1097        $self->{_acl_cache}{uid} ||= '';
1098
1099        if ($self->{_acl_cache}{uid} ne $who) {
1100            $self->{_acl_cache}{uid} = $who;
1101            my $uo = $self->get_object('user', $who);
1102            $self->{_acl_cache}{obj} = $uo;
1103            if ($uo) {
1104                $self->{_acl_cache}{groups} = [ $uo->_get_attributes('memberOf') ];
1105            }
1106        }
1107
1108        if ($who && (my $uo = $self->{_acl_cache}{obj})) {
1109            $groups = $self->{_acl_cache}{groups};
1110        } else {
1111            $who = '';
1112        }
1113        my $res = $self->{_acls}->check($obj, $attr, $perm, $who, $groups);
1114        $self->log(LA_INFO, 'permission denied for "%s" to get %s.%s for %s',
1115           $who, ref $obj ? $obj->id . '(' . $obj->type . ')' : $obj, $attr, $perm) if (!$res);
1116        return $res;
1117    } else {
1118        # No acls, woot
1119        return 1;
1120    }
1121}
1122
1123=head2 text_empty_dump($fh, $otype, $options)
1124
1125Empty object dump
1126
1127=cut
1128
1129sub text_empty_dump {
1130    my ($self, $fh, $otype, $options) = @_;
1131    my $pclass = $self->_load_obj_class($otype) or return;
1132    $pclass->text_dump($fh, $options, $self);
1133}
1134
11351;
1136
1137__END__
1138
1139=head1 SEE ALSO
1140
1141=head1 AUTHOR
1142
1143Thauvin Olivier, E<lt>olivier.thauvin@latmos.ipsl.fr<gt>
1144
1145=head1 COPYRIGHT AND LICENSE
1146
1147Copyright (C) 2009 by Thauvin Olivier
1148
1149This library is free software; you can redistribute it and/or modify
1150it under the same terms as Perl itself, either Perl version 5.10.0 or,
1151at your option, any later version of Perl 5 you may have available.
1152
1153=cut
Note: See TracBrowser for help on using the repository browser.