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

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

kill debog warn()

  • Property svn:keywords set to Id Rev
File size: 26.6 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            $self->log(LA_ERR, "Cannot create $otype, wrong value");
338            return;
339        };
340    }
341
342    # populating default value
343    {
344        my %default = $self->compute_default($otype, $id, %cdata);
345        foreach my $k (keys %default) {
346            $cdata{$k} = $default{$k};
347        }
348    }
349
350    my %data;
351    foreach my $cfield (keys %cdata) {
352        my $attribute = $self->attribute($otype, $cfield) or next;
353        $attribute->ro and next;
354        $data{$attribute->iname} = $cdata{$cfield};
355    }
356    $self->create_object($otype, $id, %data) or return;
357    my $obj = $self->get_object($otype, $id) or return;
358    $obj->ReportChange('Create', 'Object created with %s', join(', ', sort keys %cdata));
359
360    foreach my $attrname (keys %data) {
361        my $attribute = $self->attribute($obj->type, $attrname) or next;
362        $attribute->monitored or next;
363
364        $obj->ReportChange('Attributes', '%s set to %s', $attrname,
365            (ref $data{$attrname}
366                ? join(', ', @{ $data{$attrname} })
367                : $data{$attrname}) || '(none)');
368    }
369
370    $obj
371}
372
373=head2 create_object($type, $id, %data)
374
375Create and return an object of type $type with unique id
376$id having %data.
377
378This method should be provided by the data base handler.
379
380=cut
381
382sub create_object {
383    my ($self, $otype, $id, %data) = @_;
384    "$id" or do {
385        $self->log(LA_ERR, "Cannot create %s object with empty id",
386            $otype);
387        return;
388    };
389    my $pclass = $self->_load_obj_class($otype) or do {
390        $self->log(LA_ERR, "Cannot create %s object type (cannot load class)",
391            $otype);
392        return;
393    };
394
395    if (!$pclass->checkValues($self, $id, %data)) {
396        la_log(LA_ERR,
397            'Cannot create %s (%s) in base %s (%s): wrong values',
398            $id, $otype, $self->label, $self->type
399        );
400        return;
401    }
402
403    if ($pclass->_create($self, $id, %data)) {
404        la_log(LA_INFO,
405            'Object %s (%s) created in base %s (%s)',
406            $id, $otype, $self->label, $self->type
407        );
408    } else {
409        la_log(LA_ERR,
410            'Object creation %s (%s) in base %s (%s) failed',
411            $id, $otype, $self->label, $self->type
412        );
413        return;
414    };
415
416    $self->get_object($otype, $id);
417}
418
419=head2 defaultAttributeValue($otype, $attr)
420
421Return default static value for attribute C<$attr>
422
423=cut
424
425sub defaultAttributeValue {
426    my ($self, $otype, $attr) = @_;
427
428    my %def = %{ $self->{defattr} || {}};
429    return $def{"$otype.$attr"} || '';
430}
431
432=head2 compute_default($otype, $id, %cdata)
433
434Return a hash containing value to set for new object
435
436=cut
437
438sub compute_default {
439    my ($self, $otype, $id, %cdata) = @_;
440
441    my %default;
442    foreach my $def (keys %{ $self->{defattr} || {}}) {
443        if ($def =~ /^$otype\.(.*)$/) {
444            $default{$1} = $self->{defattr}{$def} if(!$cdata{$1});
445        }
446    }
447
448    # computed default value (not a simple set)
449    if (lc($otype) eq 'user') {
450        if (!$cdata{homeDirectory}) {
451            $default{homeDirectory} = $self->{defattr}{'user.homebase'}
452                ? $self->{defattr}{'user.homebase'} . "/$id" 
453                : '';
454        }
455
456        if (!$cdata{uidNumber}) {
457            $default{uidNumber} ||= $self->find_next_numeric_id('user', 'uidNumber',
458            $self->{defattr}{'user.min_uid'}, $self->{defattr}{'user.max_uid'});
459        }
460
461        my $mailid = $cdata{givenName} && $cdata{sn}
462            ? sprintf('%s.%s',
463                to_ascii(lc($cdata{givenName})),
464                to_ascii(lc($cdata{sn})),)
465            : undef;
466        $mailid =~ s/\s+/-/g if($mailid);
467
468        if ($mailid &&
469            $self->is_supported_object('aliases') &&
470            ! $self->get_object('aliases', $mailid)) {
471            if (my $attr = $self->attribute($otype, 'mail')) {
472                if ((!$attr->ro) && $self->{defattr}{'user.maildomain'}) {
473                    $default{mail} ||= sprintf('%s@%s',
474                    $mailid,
475                    $self->{defattr}{'user.maildomain'});
476                }
477            }
478            if (my $attr = $self->attribute($otype, 'aliases')) {
479                $default{aliases} ||= $mailid unless ($attr->ro);
480            }
481            if (my $attr = $self->attribute($otype, 'revaliases')) {
482                $default{revaliases} ||= $mailid unless ($attr->ro);
483            }
484        }
485    } elsif (lc($otype) eq 'group') {
486        if (!$cdata{gidNumber}) {
487            $default{gidNumber} ||= $self->find_next_numeric_id(
488                'group', 'gidNumber',
489                $self->{defattr}{'group.min_gid'},
490                $self->{defattr}{'group.max_gid'}
491            );
492        }
493    }
494
495    return %default;
496}
497
498sub _allowed_values {
499    $_[0]->{_allowed_values}
500}
501
502=head2 obj_attr_allowed_values ($otype, $attr)
503
504Return value allowed for this attribute
505
506=cut
507
508sub obj_attr_allowed_values {
509    my ($self, $otype, $attr) = @_;
510    if ($self->_allowed_values &&
511        $self->_allowed_values->SectionExists("$otype.$attr")) {
512        return grep { defined($_) } $self->_allowed_values->val("$otype.$attr", 'allowed');
513    }
514    return();
515}
516
517=head2 check_allowed_values ($otype, $attr, $attrvalues)
518
519Check attributes C<$attr> of object type C<$otype> allow values C<$attrvalues>
520
521=cut
522
523sub check_allowed_values {
524    my ($self, $otype, $attr, $attrvalues) = @_;
525    $self->_allowed_values or return 1;
526    my @values = ref $attrvalues ? @{ $attrvalues } : $attrvalues;
527    foreach my $value (@values) {
528        $value or next;
529        if (my @allowed = $self->obj_attr_allowed_values($otype, $attr)) {
530            grep { $value eq $_ } @allowed or do {
531                $self->log(LA_ERR,
532                    "value `%s' is not allow for %s.%s per configuration (allowed_values)",
533                    $value, $otype, $attr
534                );
535                return;
536            };
537        }
538    }
539    return 1;
540}
541
542=head2 list_canonical_fields($otype, $for)
543
544Return the list of supported fields by the database for object type $otype.
545
546Optionnal $for specify the goal for which the list is requested, only supported
547fields will be returns
548
549=cut
550
551sub list_canonical_fields {
552    my ($self, $otype, $for) = @_;
553    $for ||= 'rw';
554    my $pclass = $self->_load_obj_class($otype) or return;
555    sort $pclass->_canonical_fields($self, $for);
556}
557
558sub _get_attr_schema {
559    my ($self, $otype) = @_;
560    my $pclass = $self->_load_obj_class($otype) or return;
561    return $pclass->_get_attr_schema($self);
562}
563
564=head2 get_attr_schema
565
566Deprecated
567
568=cut
569
570# TODO: kill this
571
572sub get_attr_schema {
573    my ($self, $otype, $attribute) = @_;
574    my $info = $self->_get_attr_schema($otype);
575    if ($info->{$attribute}) {
576        return $info->{$attribute};
577    } else {
578        return;
579    }
580}
581
582=head2 attribute($otype, $attribute)
583
584Return attribute object.
585
586See L<LATMOS::Accounts::Bases::Attribute>
587
588=cut
589
590sub attribute {
591    my ($self, $otype, $attribute) = @_;
592
593    my $attrinfo;
594    if (!ref $attribute) {
595       $attrinfo = $self->get_attr_schema($otype, $attribute)
596            or return;
597        $attrinfo->{name} = $attribute;
598    } else {
599        $attrinfo = $attribute;
600    }
601
602    return LATMOS::Accounts::Bases::Attributes->new(
603        $attrinfo,
604        $self,
605        $otype,
606    );
607}
608
609=head2 delayed_fields
610
611DEPRECATED
612
613=cut 
614
615# TODO: kill this
616
617sub delayed_fields {
618    my ($self, $otype, $for) = @_;
619    $self->log(LA_WARN, "calling DEPRECATED delayed_fields " . join(',',
620            caller));
621    $for ||= 'rw';
622    my @attrs;
623    foreach ($self->list_canonical_fields($otype, $for)) {
624        my $attr = $self->attribute($otype, $_) or next;
625        $for =~ /w/ && $attr->ro and next;
626        $attr->delayed or next;
627        push(@attrs, $_);
628    }
629    @attrs
630}
631
632=head2 ochelper ($otype)
633
634Return L<LATMOS::Accounts::Bases::OChelper> object
635
636=cut
637
638sub ochelper {
639    my ($self, $otype) = @_;
640    my $pclass = ucfirst(lc($otype));
641    foreach my $class (
642        ref($self) . '::OCHelper::' . $pclass,
643        ref($self) . '::OCHelper',
644        "LATMOS::Accounts::Bases::OCHelper::$pclass",
645        'LATMOS::Accounts::Bases::OCHelper' ) {
646        eval "require $class;";
647        if ($@) { next } # error message ?
648        my $ochelper = "$class"->new($self, $otype);
649        return $ochelper;
650    }
651    return;
652}
653
654=head2 delete_object($otype, $id)
655
656Destroy from data base object type $otype having id $id.
657
658=cut
659
660sub delete_object {
661    my ($self, $otype, $id) = @_;
662    my $obj = $self->get_object($otype, $id) or do {
663        $self->log(LA_WARN, 'Cannot delete %s/%s: no such object',
664            $otype, $id);
665        return;
666    };
667    $self->check_acl($obj, '@DELETE', 'w') or do {
668        $self->log(LA_WARN, 'permission denied to delete %s/%s',
669            $otype, $id);
670        return;
671    };
672    my $ref = $obj->Iid;
673    if (my $res = $self->_delete_object($otype, $id)) {
674        $self->ReportChange($otype, $id, $ref, 'Delete', 'Object deleted');
675        return $res;
676    }
677    return;
678}
679
680sub _delete_object {
681    my ($self, $otype, $id) = @_;
682    my $pclass = $self->_load_obj_class($otype);
683    $pclass->_delete($self, $id);
684}
685
686=head2 rename_object($otype, $id, $newid)
687
688Rename an object.
689
690=cut
691
692sub rename_object {
693    my ($self, $otype, $id, $newid) = @_;
694
695    my $obj = $self->get_object($otype, $id) or do {
696        $self->log(LA_WARN, 'Cannot rename %s/%s: no such object',
697            $otype, $id);
698        return;
699    };
700    if (my $chk = (lc($otype) eq 'user' || lc($otype) eq 'group')
701        ? LATMOS::Accounts::Utils::check_ug_validity($newid)
702        : LATMOS::Accounts::Utils::check_oid_validity($newid)) {
703        $self->log(LA_ERR, "Cannot rename $otype/$id to ID $newid `%s:'", $chk);
704        return;
705    }
706    $self->check_acl($obj, '@DELETE', 'w') &&
707    $self->check_acl($obj, '@CREATE', 'w') or do {
708        $self->log(LA_WARN, 'permission denied to rename %s/%s',
709            $otype, $id);
710        return;
711    };
712
713    my $oldref = $obj->Iid;
714
715    if (my $res = $self->_rename_object($otype, $id, $newid)) {
716        my $newobj = $self->get_object($otype, $newid) or do {
717            $self->log(LA_WARN, 'Cannot get object %s/%s: rename failed ?',
718                $otype, $id);
719            return;
720        };
721
722        $self->ReportChange($otype, $id, $oldref, 'Rename', 'Object rename to %s', $newid);
723        $newobj->ReportChange('Rename', 'Object renamed from %s', $id);
724        return $res;
725    }
726    return;
727}
728
729sub _rename_object {
730    my ($self, $otype, $id, $newid) = @_;
731    my $pclass = $self->_load_obj_class($otype);
732    $pclass->can('_rename') or do {
733        $self->log(LA_ERR, 'rename object type %s is unsupported', $otype);
734        return;
735    };
736    $pclass->_rename($self, $id, $newid);
737}
738
739=head2 is_transactionnal
740
741Return True is the database support commit and rollback
742
743=cut
744
745sub is_transactionnal {
746    my ($self) = @_;
747    return($self->can('_rollback') && $self->can('_commit'));
748}
749
750=head2 commit
751
752Save change into the database if change are not done immediately.
753This should always be called as you don't know when change are applied.
754
755Return always true if database does not support any transaction.
756
757The driver should provides a _commit functions to save data.
758
759=cut
760
761sub commit {
762    my ($self) = @_;
763    if ($self->can('_commit')) {
764        la_log(LA_DEBUG, 'Commiting data');
765        if (!(my $res = $self->_commit)) {
766            la_log(LA_ERR, "Commit error on %s", $_->label);
767            return $res;
768        }
769    }
770
771    $self->postcommit();
772
773    return 1;
774}
775
776=head2 postcommit
777
778Run postcommit command
779
780=cut
781
782sub postcommit {
783    my ($self) = @_;
784
785    if ($self->{_options}{postcommit}) {
786        exec_command($self->{_options}{postcommit},
787            {
788                BASE => $self->label,
789                BASETYPE => $self->type,
790                HOOK_TYPE => 'POST',
791                CONFIG => $self->{_options}{configdir}, 
792            }
793        );
794    } else {
795        return 1;
796    }
797}
798
799=head2 rollback
800
801If database support transaction, rollback changes. Return false
802if database does not support.
803
804If supported, driver should provides a _rollback functions
805
806=cut
807
808sub rollback {
809    my ($self) = @_;
810    if ($self->can('_rollback')) {
811       la_log(LA_DEBUG, 'Rolling back data');
812       return $self->_rollback;
813   } else {
814       return 0;
815   }
816}
817
818=head2 current_rev
819
820Return the current revision of the database
821
822Must be provide by base driver if incremental synchro is supported
823
824=cut
825
826sub current_rev { return }
827
828=head2 list_objects_from_rev($otype, $rev)
829
830Return the list of UID for object of $otype.
831
832=cut
833
834sub list_objects_from_rev {
835    my ($self, $otype, $rev) = @_;
836    my $pclass = $self->_load_obj_class($otype) or return;
837    if (defined($rev) && $pclass->can('list_from_rev')) {
838        return $pclass->list_from_rev($self, $rev);
839    } else {
840        # no support, return all objects...
841        return $self->list_objects($otype);
842    }
843}
844
845=head2 sync_object_from($srcbase, $otype, $id, %options)
846
847Sync object type C<$otype> C<$id> from base C<$srcbase> to current base.
848
849C<%options>:
850
851=over 4
852
853=item nodelete
854
855Don't delete object if the object synchronize don't exist in source base
856
857=back
858
859=cut
860
861sub sync_object_from {
862    my ($self, $srcbase, $otype, $id, %options) = @_;
863
864    # is the object type supported by both
865    foreach ($self, $srcbase) {
866        $_->is_supported_object($otype) or return '';
867    }
868   
869    if (my $srcobj = $srcbase->get_object($otype, $id)) {
870        return $self->sync_object($srcobj, %options);
871    } elsif (!$options{nodelete}) {
872        $self->_delete_object($otype, $id) and return 'DELETED';
873    }
874    return;
875}
876
877=head2 sync_object
878
879Synchronise an object into this base
880
881=cut
882
883sub sync_object {
884    my ($self, $srcobj, %options) = @_;
885    $self->is_supported_object($srcobj->type) or return '';
886    my @fields = $options{attrs}
887        ? @{ $options{attrs} }
888        : $self->list_canonical_fields($srcobj->type, 'w');
889    my %data;
890    foreach (@fields) {
891        # check attribute exists in source:
892        my $attr = $srcobj->attribute($_) or next;
893        $attr->readable or next;
894        if (! $options{onepass}) {
895            if ($options{firstpass}) {
896                $attr->delayed and next;
897            } else {
898                $attr->delayed or next;
899            }
900        }
901        $data{$_} = $srcobj->_get_c_field($_);
902    }
903    if (my $dstobj = $self->get_object($srcobj->type, $srcobj->id)) {
904        keys %data or return 'SYNCED';
905        foreach (keys %data) {
906            if (!$dstobj->attribute($_) ||
907                $dstobj->attribute($_)->ro) {
908                delete($data{$_});
909            }
910        }
911        my $res = $dstobj->_set_c_fields(%data);
912        if (defined $res) {
913            return $res ? 'SYNCED' : '';
914        } else {
915            return;
916        }
917    } elsif(!$options{nocreate}) {
918        if ((! $options{firstpass}) && (!$options{onepass})) {
919            $self->log(LA_ERR, 'This is not first pass, creation wanted but denied');
920            return;
921        }
922        if (my $res = $self->_create_c_object($srcobj->type, $srcobj->id, %data)) {
923            return 'CREATED'
924        } else {
925            return;
926        }
927    } else {
928        # No error, but creation is denied
929        return 'Creation skipped';
930    }
931
932    return;
933}
934
935=head2 search_objects($otype, @filter)
936
937Search object according @filter. @filter is a list
938of field/value which should match.
939
940A default function is provided but each db driver can provide
941an optimize version.
942
943=cut
944
945sub search_objects {
946    my ($self, $otype, @filter) = @_;
947    my $pclass = $self->_load_obj_class($otype) or return;
948    $pclass->search($self, @filter);
949}
950
951=head2 attributes_summary($otype, $attr)
952
953Return couple object id / value for attribute C<$attr> of object type C<$otype>
954
955This method is designed to be faster than fetching object one by one.
956
957=cut
958
959sub attributes_summary {
960    my ($self, $otype, $attr) = @_;
961    my $pclass = $self->_load_obj_class($otype) or return;
962    $pclass->attributes_summary($self, $attr);
963}
964
965=head2 attributes_summary_by_object($otype, $attr)
966
967Return couple object id / value for attribute C<$attr> of object type C<$otype>
968
969This method is designed to be faster than fetching object one by one.
970
971=cut
972
973sub attributes_summary_by_object {
974    my ($self, $otype, $attr) = @_;
975    my $pclass = $self->_load_obj_class($otype) or return;
976    $pclass->attributes_summary_by_object($self, $attr);
977}
978
979=head2 find_next_numeric_id($otype, $field, $min, $max)
980
981Return, if possible, next numeric id available (typically unix user UID).
982
983=cut
984
985sub find_next_numeric_id {
986    my ($self, $otype, $field, $min, $max) = @_;
987    my $pclass = $self->_load_obj_class($otype) or return;
988    $pclass->find_next_numeric_id($self, $field, $min, $max);
989}
990
991=head2 authenticate_user($username, $passwd)
992
993Return true if authentication success.
994
995Must be override by driver if the base have a proper authentication method
996
997=cut
998
999sub authenticate_user {
1000    my ($self, $username, $passwd) = @_;
1001    $username or return;
1002    my $uobj = $self->get_object('user', $username) or do {
1003        la_log(LA_ERR, "Cannot authenticate non existing user $username");
1004        return;
1005    };
1006
1007    if ($uobj->_get_c_field('expired')) {
1008        la_log(LA_ERR, "Account $username has expired (%s)",
1009            $uobj->_get_c_field('expired'));
1010        return;
1011    }
1012
1013    if ($uobj->_get_c_field('locked')) {
1014        la_log(LA_ERR, "Account $username is currently locked");
1015        return;
1016    }
1017
1018    my $password = $uobj->get_field('userPassword') or do {
1019        la_log(LA_ERR, "Cannot authenticate user $username having no passwd");
1020        return;
1021    };
1022    if ($password eq crypt($passwd, $password)) { # crypt unix
1023        la_log(LA_NOTICE, "User $username authenticated");
1024        return 1;
1025    } else {
1026        la_log(LA_ERR, "Cannot authenticate user $username");
1027        return 0;
1028    }
1029}
1030
1031=head2 connect($username, $password)
1032
1033Authenticate the user and store the username as connected
1034
1035=cut
1036
1037sub connect {
1038    my ($self, $username, $password) = @_;
1039    my $auth = $self->authenticate_user($username, $password);
1040    if ($auth) {
1041        $self->{_user} = $username;
1042        la_log(LA_DEBUG, "Connected as $username");
1043    }
1044    return $auth;
1045}
1046
1047=head2 user
1048
1049Return the current connected username
1050
1051=cut
1052
1053sub user { $_[0]->{_user} }
1054
1055=head2 check_acl($obj, $attr, $perm)
1056
1057Return true if connected user have C<$perm> permission on attribute C<$attr> of
1058object C<$obj>.
1059
1060=cut
1061
1062sub check_acl {
1063    my ($self, $obj, $attr, $perm) = @_;
1064    if ($self->{_acls}) {
1065        my ($who, $groups) = ($self->user || '');
1066        if ($who && (my $uo = $self->get_object('user', $who))) {
1067            $groups = [ $uo->_get_attributes('memberOf') ];
1068        } else {
1069            $who = '';
1070        }
1071        my $res = $self->{_acls}->check($obj, $attr, $perm, $who, $groups);
1072        $self->log(LA_INFO, 'permission denied for "%s" to get %s.%s for %s',
1073           $who, ref $obj ? $obj->id . '(' . $obj->type . ')' : $obj, $attr, $perm) if (!$res);
1074        return $res;
1075    } else {
1076        # No acls, woot
1077        return 1;
1078    }
1079}
1080
1081=head2 text_empty_dump($fh, $otype, $options)
1082
1083Empty object dump
1084
1085=cut
1086
1087sub text_empty_dump {
1088    my ($self, $fh, $otype, $options) = @_;
1089    my $pclass = $self->_load_obj_class($otype) or return;
1090    $pclass->text_dump($fh, $options, $self);
1091}
1092
10931;
1094
1095__END__
1096
1097=head1 SEE ALSO
1098
1099=head1 AUTHOR
1100
1101Thauvin Olivier, E<lt>olivier.thauvin@latmos.ipsl.fr<gt>
1102
1103=head1 COPYRIGHT AND LICENSE
1104
1105Copyright (C) 2009 by Thauvin Olivier
1106
1107This library is free software; you can redistribute it and/or modify
1108it under the same terms as Perl itself, either Perl version 5.10.0 or,
1109at your option, any later version of Perl 5 you may have available.
1110
1111=cut
Note: See TracBrowser for help on using the repository browser.