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

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

log attributes changes done at object creation when notify is set

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