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

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

Refuse object creation if post-setting attribute failed

  • Property svn:keywords set to Id Rev
File size: 26.9 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};
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    my $obj = $self->get_object($otype, $id) or do {
702        $self->log(LA_WARN, 'Cannot rename %s/%s: no such object',
703            $otype, $id);
704        return;
705    };
706    if (my $chk = (lc($otype) eq 'user' || lc($otype) eq 'group')
707        ? LATMOS::Accounts::Utils::check_ug_validity($newid)
708        : LATMOS::Accounts::Utils::check_oid_validity($newid)) {
709        $self->log(LA_ERR, "Cannot rename $otype/$id to ID $newid `%s:'", $chk);
710        return;
711    }
712    $self->check_acl($obj, '@DELETE', 'w') &&
713    $self->check_acl($obj, '@CREATE', 'w') or do {
714        $self->log(LA_WARN, 'permission denied to rename %s/%s',
715            $otype, $id);
716        return;
717    };
718
719    my $oldref = $obj->Iid;
720
721    if (my $res = $self->_rename_object($otype, $id, $newid)) {
722        my $newobj = $self->get_object($otype, $newid) or do {
723            $self->log(LA_WARN, 'Cannot get object %s/%s: rename failed ?',
724                $otype, $id);
725            return;
726        };
727
728        $self->ReportChange($otype, $id, $oldref, 'Rename', 'Object rename to %s', $newid);
729        $newobj->ReportChange('Rename', 'Object renamed from %s', $id);
730        return $res;
731    }
732    return;
733}
734
735sub _rename_object {
736    my ($self, $otype, $id, $newid) = @_;
737    my $pclass = $self->_load_obj_class($otype);
738    $pclass->can('_rename') or do {
739        $self->log(LA_ERR, 'rename object type %s is unsupported', $otype);
740        return;
741    };
742    $pclass->_rename($self, $id, $newid);
743}
744
745=head2 is_transactionnal
746
747Return True is the database support commit and rollback
748
749=cut
750
751sub is_transactionnal {
752    my ($self) = @_;
753    return($self->can('_rollback') && $self->can('_commit'));
754}
755
756=head2 commit
757
758Save change into the database if change are not done immediately.
759This should always be called as you don't know when change are applied.
760
761Return always true if database does not support any transaction.
762
763The driver should provides a _commit functions to save data.
764
765=cut
766
767sub commit {
768    my ($self) = @_;
769    if ($self->can('_commit')) {
770        la_log(LA_DEBUG, 'Commiting data');
771        if (!(my $res = $self->_commit)) {
772            la_log(LA_ERR, "Commit error on %s", $_->label);
773            return $res;
774        }
775    }
776
777    $self->postcommit();
778
779    return 1;
780}
781
782=head2 postcommit
783
784Run postcommit command
785
786=cut
787
788sub postcommit {
789    my ($self) = @_;
790
791    if ($self->{_options}{postcommit}) {
792        exec_command($self->{_options}{postcommit},
793            {
794                BASE => $self->label,
795                BASETYPE => $self->type,
796                HOOK_TYPE => 'POST',
797                CONFIG => $self->{_options}{configdir}, 
798            }
799        );
800    } else {
801        return 1;
802    }
803}
804
805=head2 rollback
806
807If database support transaction, rollback changes. Return false
808if database does not support.
809
810If supported, driver should provides a _rollback functions
811
812=cut
813
814sub rollback {
815    my ($self) = @_;
816    if ($self->can('_rollback')) {
817       la_log(LA_DEBUG, 'Rolling back data');
818       return $self->_rollback;
819   } else {
820       return 0;
821   }
822}
823
824=head2 current_rev
825
826Return the current revision of the database
827
828Must be provide by base driver if incremental synchro is supported
829
830=cut
831
832sub current_rev { return }
833
834=head2 list_objects_from_rev($otype, $rev)
835
836Return the list of UID for object of $otype.
837
838=cut
839
840sub list_objects_from_rev {
841    my ($self, $otype, $rev) = @_;
842    my $pclass = $self->_load_obj_class($otype) or return;
843    if (defined($rev) && $pclass->can('list_from_rev')) {
844        return $pclass->list_from_rev($self, $rev);
845    } else {
846        # no support, return all objects...
847        return $self->list_objects($otype);
848    }
849}
850
851=head2 sync_object_from($srcbase, $otype, $id, %options)
852
853Sync object type C<$otype> C<$id> from base C<$srcbase> to current base.
854
855C<%options>:
856
857=over 4
858
859=item nodelete
860
861Don't delete object if the object synchronize don't exist in source base
862
863=back
864
865=cut
866
867sub sync_object_from {
868    my ($self, $srcbase, $otype, $id, %options) = @_;
869
870    # is the object type supported by both
871    foreach ($self, $srcbase) {
872        $_->is_supported_object($otype) or return '';
873    }
874   
875    if (my $srcobj = $srcbase->get_object($otype, $id)) {
876        return $self->sync_object($srcobj, %options);
877    } elsif (!$options{nodelete}) {
878        $self->_delete_object($otype, $id) and return 'DELETED';
879    }
880    return;
881}
882
883=head2 sync_object
884
885Synchronise an object into this base
886
887=cut
888
889sub sync_object {
890    my ($self, $srcobj, %options) = @_;
891    $self->is_supported_object($srcobj->type) or return '';
892    my @fields = $options{attrs}
893        ? @{ $options{attrs} }
894        : $self->list_canonical_fields($srcobj->type, 'w');
895    my %data;
896    foreach (@fields) {
897        # check attribute exists in source:
898        my $attr = $srcobj->attribute($_) or next;
899        $attr->readable or next;
900        if (! $options{onepass}) {
901            if ($options{firstpass}) {
902                $attr->delayed and next;
903            } else {
904                $attr->delayed or next;
905            }
906        }
907        $data{$_} = $srcobj->_get_c_field($_);
908    }
909    if (my $dstobj = $self->get_object($srcobj->type, $srcobj->id)) {
910        keys %data or return 'SYNCED';
911        foreach (keys %data) {
912            if (!$dstobj->attribute($_) ||
913                $dstobj->attribute($_)->ro) {
914                delete($data{$_});
915            }
916        }
917        my $res = $dstobj->_set_c_fields(%data);
918        if (defined $res) {
919            return $res ? 'SYNCED' : '';
920        } else {
921            return;
922        }
923    } elsif(!$options{nocreate}) {
924        if ((! $options{firstpass}) && (!$options{onepass})) {
925            $self->log(LA_ERR, 'This is not first pass, creation wanted but denied');
926            return;
927        }
928        if (my $res = $self->_create_c_object($srcobj->type, $srcobj->id, %data)) {
929            return 'CREATED'
930        } else {
931            return;
932        }
933    } else {
934        # No error, but creation is denied
935        return 'Creation skipped';
936    }
937
938    return;
939}
940
941=head2 search_objects($otype, @filter)
942
943Search object according @filter. @filter is a list
944of field/value which should match.
945
946A default function is provided but each db driver can provide
947an optimize version.
948
949=cut
950
951sub search_objects {
952    my ($self, $otype, @filter) = @_;
953    my $pclass = $self->_load_obj_class($otype) or return;
954    $pclass->search($self, @filter);
955}
956
957=head2 attributes_summary($otype, $attr)
958
959Return couple object id / value for attribute C<$attr> of object type C<$otype>
960
961This method is designed to be faster than fetching object one by one.
962
963=cut
964
965sub attributes_summary {
966    my ($self, $otype, $attr) = @_;
967    my $pclass = $self->_load_obj_class($otype) or return;
968    $pclass->attributes_summary($self, $attr);
969}
970
971=head2 attributes_summary_by_object($otype, $attr)
972
973Return couple object id / value for attribute C<$attr> of object type C<$otype>
974
975This method is designed to be faster than fetching object one by one.
976
977=cut
978
979sub attributes_summary_by_object {
980    my ($self, $otype, $attr) = @_;
981    my $pclass = $self->_load_obj_class($otype) or return;
982    $pclass->attributes_summary_by_object($self, $attr);
983}
984
985=head2 find_next_numeric_id($otype, $field, $min, $max)
986
987Return, if possible, next numeric id available (typically unix user UID).
988
989=cut
990
991sub find_next_numeric_id {
992    my ($self, $otype, $field, $min, $max) = @_;
993    my $pclass = $self->_load_obj_class($otype) or return;
994    $pclass->find_next_numeric_id($self, $field, $min, $max);
995}
996
997=head2 authenticate_user($username, $passwd)
998
999Return true if authentication success.
1000
1001Must be override by driver if the base have a proper authentication method
1002
1003=cut
1004
1005sub authenticate_user {
1006    my ($self, $username, $passwd) = @_;
1007    $username or return;
1008    my $uobj = $self->get_object('user', $username) or do {
1009        la_log(LA_ERR, "Cannot authenticate non existing user $username");
1010        return;
1011    };
1012
1013    if ($uobj->_get_c_field('expired')) {
1014        la_log(LA_ERR, "Account $username has expired (%s)",
1015            $uobj->_get_c_field('expired'));
1016        return;
1017    }
1018
1019    if ($uobj->_get_c_field('locked')) {
1020        la_log(LA_ERR, "Account $username is currently locked");
1021        return;
1022    }
1023
1024    my $password = $uobj->get_field('userPassword') or do {
1025        la_log(LA_ERR, "Cannot authenticate user $username having no passwd");
1026        return;
1027    };
1028    if ($password eq crypt($passwd, $password)) { # crypt unix
1029        la_log(LA_NOTICE, "User $username authenticated");
1030        return 1;
1031    } else {
1032        la_log(LA_ERR, "Cannot authenticate user $username");
1033        return 0;
1034    }
1035}
1036
1037=head2 connect($username, $password)
1038
1039Authenticate the user and store the username as connected
1040
1041=cut
1042
1043sub connect {
1044    my ($self, $username, $password) = @_;
1045    my $auth = $self->authenticate_user($username, $password);
1046    if ($auth) {
1047        $self->{_user} = $username;
1048        la_log(LA_DEBUG, "Connected as $username");
1049    }
1050    return $auth;
1051}
1052
1053=head2 user
1054
1055Return the current connected username
1056
1057=cut
1058
1059sub user { $_[0]->{_user} }
1060
1061=head2 check_acl($obj, $attr, $perm)
1062
1063Return true if connected user have C<$perm> permission on attribute C<$attr> of
1064object C<$obj>.
1065
1066=cut
1067
1068sub check_acl {
1069    my ($self, $obj, $attr, $perm) = @_;
1070    if ($self->{_acls}) {
1071        my ($who, $groups) = ($self->user || '');
1072        if ($who && (my $uo = $self->get_object('user', $who))) {
1073            $groups = [ $uo->_get_attributes('memberOf') ];
1074        } else {
1075            $who = '';
1076        }
1077        my $res = $self->{_acls}->check($obj, $attr, $perm, $who, $groups);
1078        $self->log(LA_INFO, 'permission denied for "%s" to get %s.%s for %s',
1079           $who, ref $obj ? $obj->id . '(' . $obj->type . ')' : $obj, $attr, $perm) if (!$res);
1080        return $res;
1081    } else {
1082        # No acls, woot
1083        return 1;
1084    }
1085}
1086
1087=head2 text_empty_dump($fh, $otype, $options)
1088
1089Empty object dump
1090
1091=cut
1092
1093sub text_empty_dump {
1094    my ($self, $fh, $otype, $options) = @_;
1095    my $pclass = $self->_load_obj_class($otype) or return;
1096    $pclass->text_dump($fh, $options, $self);
1097}
1098
10991;
1100
1101__END__
1102
1103=head1 SEE ALSO
1104
1105=head1 AUTHOR
1106
1107Thauvin Olivier, E<lt>olivier.thauvin@latmos.ipsl.fr<gt>
1108
1109=head1 COPYRIGHT AND LICENSE
1110
1111Copyright (C) 2009 by Thauvin Olivier
1112
1113This library is free software; you can redistribute it and/or modify
1114it under the same terms as Perl itself, either Perl version 5.10.0 or,
1115at your option, any later version of Perl 5 you may have available.
1116
1117=cut
Note: See TracBrowser for help on using the repository browser.