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

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

Fix I18N

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