source: branches/4.0/LATMOS-Accounts/lib/LATMOS/Accounts/Bases.pm @ 1299

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

backport fix

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