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

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

review the way chanages are report, make it more general

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