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

Last change on this file since 1315 was 1307, checked in by nanardon, 9 years ago

Reorder some code for logic

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