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

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

Split authentication function per base fonctionnality

  • Property svn:keywords set to Id Rev
File size: 25.7 KB
Line 
1package LATMOS::Accounts::Bases;
2
3use 5.010000;
4use strict;
5use warnings;
6use LATMOS::Accounts::Bases::Objects;
7use LATMOS::Accounts::Bases::Attributes;
8use LATMOS::Accounts::Log;
9use LATMOS::Accounts::Utils qw(exec_command to_ascii);
10
11our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
12
13=head1 NAME
14
15LATMOS::Accounts::Bases - Base class for account data bases
16
17=head1 SYNOPSIS
18
19  use LATMOS::Accounts::Bases;
20  my $base = LATMOS::Accounts::Bases->new('type', %options);
21  ...
22
23=head1 DESCRIPTION
24
25This module provide basic functions for various account base
26
27=head1 FUNCTIONS
28
29=cut
30
31=head2 new($type, %options)
32
33Return, if success, a new data base account object, $type is
34account base type, %options to setup the base.
35
36=cut
37
38sub new {
39    my ($class, $type, $options) = @_;
40
41    my $pclass = ucfirst(lc($type));
42    eval "require LATMOS::Accounts::Bases::$pclass;";
43    if ($@) {
44        la_log(LA_DEBUG, "Failed to load base type `%s': %s", $type, $@);
45        return
46    }
47    my $base = "LATMOS::Accounts::Bases::$pclass"->new(%{$options->{params}})
48        or return;
49    $base->{_type} = lc($pclass);
50    $base->{_label} = $options->{label};
51    $base->{_options} = $options->{params};
52    $base->{wexported} = 0;
53    $base->{defattr} = $options->{defattr};
54    $base->{_acls} = $options->{acls};
55    $base->{_allowed_values} = $options->{allowed_values};
56    $base->{_la} = $options->{la};
57    la_log(LA_DEBUG, 'Instanciate base %s (%s)', ($base->label || 'N/A'), $pclass);
58    $base
59}
60
61=head2 load
62
63Make account base loading data into memory if need.
64Should always be called, if database fetch data on the fly
65(SQL, LDAP), the function just return True.
66
67=cut
68
69# override by the database driver if need
70sub load { 1 }
71
72=head2 label
73
74Return the database label
75
76=cut
77
78sub label {
79    $_[0]->{_label} || 'NoLabel';
80}
81
82=head2 type
83
84Return the type of the base
85
86=cut
87
88sub type {
89    $_[0]->{_type};
90}
91
92=head2 la
93
94return LATMOS::Accounts object parent to the base
95
96=cut
97
98sub la { $_[0]->{_la} };
99
100=head2 config ($opt)
101
102Return options from config
103
104=cut
105
106sub config {
107    my ($self, $opt) = @_;
108    return $self->{_options}{$opt};
109}
110
111=head2 wexported
112
113See L</unexported>
114
115=cut
116
117sub wexported { unexported(@_) }
118
119=head2 unexported ($wexported)
120
121Set base to report unexported object or not
122
123=cut
124
125sub unexported {
126    my ($self, $wexported) = @_;
127    my $old = $self->{wexported};
128    if (defined($wexported)) {
129        $self->{wexported} = $wexported;
130        $self->log(LA_DEBUG, "Switching exported mode: %s => %s", $old,
131            $wexported);
132    }
133    return($old || 0);
134}
135
136=head2 temp_switch_unexported($CODE, $value)
137
138Switch the base to unexported mode given by C<$value>, run C<$CODE>, restore
139back the previous state and return the result of code ref.
140
141=cut
142
143sub temp_switch_unexported (&;$) {
144    my ($self, $sub, $value) = @_;
145
146    my $old = $self->unexported($value || 0);
147    my $res = $sub->();
148    $self->unexported($old);
149    return $res;
150}
151
152=head2 log($level, $msg, $arg)
153
154Log a message prefixed by database information
155
156=cut
157
158sub log {
159    my ($self, $level, $msg, @args) = @_;
160    my $prefix = 'Base(' . $self->type . '/' . $self->label . ')';
161    LATMOS::Accounts::Log::la_log($level, "$prefix $msg", @args);
162}
163
164=head2 ReportChange($otype, $name, $ref, $changetype, $message, @args)
165
166Functions to report back
167
168=cut 
169
170sub ReportChange {}
171
172=head2 list_supported_objects(@otype)
173
174Return a list of supported object
175
176@type is an additionnal list of objects to check
177
178=cut
179
180sub list_supported_objects {
181    my ($self, @otype) = @_;
182    my %res;
183    foreach my $inc (@INC) {
184        my $sub = 'LATMOS::Accounts::Bases::' . ucfirst($self->type);
185        $sub =~ s/::/\//g;
186        foreach (glob("$inc/$sub/[A-Z]*.pm")) {
187            s/.*\///;
188            s/\.pm$//;
189            $res{lc($_)} = 1;
190        }
191    }
192    $res{$_} = 1 foreach(@otype);
193    my @sobj = grep { $self->is_supported_object($_) } keys %res;
194    la_log(LA_DEBUG, "Base %s supported objects: %s", $self->type, join(', ', @sobj));
195    return @sobj;
196}
197
198=head2 ordered_objects
199
200Return supported object type ordered in best order for synchronisation
201
202=cut
203
204sub ordered_objects {
205    my ($self) = @_;
206
207    my %deps;
208    my %maxdeps;
209    my @objs = sort { $b cmp $a } $self->list_supported_objects;
210    foreach my $obj (@objs) {
211        foreach my $at ($self->list_canonical_fields($obj)) {
212            my $attr = $self->attribute($obj, $at);
213            $attr->ro and next;
214            $attr->{delayed} and next;
215            if (my $res = $attr->reference) {
216                $deps{$obj}{$res} ||= 1;
217                if ($attr->mandatory) {
218                    $deps{$obj}{$res} = 2;
219                    $maxdeps{$res} = 1;
220                }
221            }
222        }
223    }
224
225    sort {
226        if (keys %{$deps{$a} || {}}) {
227            if (keys %{$deps{$b} || {}}) {
228                return (
229                    ($deps{$a}{$b} || 0) > ($deps{$b}{$a} || 0) ?  1 :
230                    ($deps{$b}{$a} || 0) > ($deps{$a}{$b} || 0) ? -1 :
231                    ($maxdeps{$b} || 0) - ($maxdeps{$a} || 0)
232                );
233            } else {
234                return 1;
235            }
236        } elsif (keys %{$deps{$b} || {}}) {
237            return -1;
238        } else {
239            return  ($maxdeps{$b} || 0) - ($maxdeps{$a} || 0)
240        }
241    } @objs;
242}
243
244sub _load_obj_class {
245    my ($self, $otype) = @_;
246
247    $otype or die "No type given: " . join(', ', caller) . "\n";
248    # finding perl class:
249    my $pclass = ref $self;
250    $pclass .= '::' . ucfirst(lc($otype));
251    eval "require $pclass;";
252    if ($@) {
253        $self->log(LA_DEBUG, 'Cannot load perl class %s', $pclass);
254        return
255    } # error message ?
256    return $pclass;
257}
258
259
260=head2 is_supported_object($otype)
261
262Return true is object type $otype is supported
263
264=cut
265
266sub is_supported_object {
267    my ($self, $otype) = @_;
268   
269    if (my $pclass = $self->_load_obj_class($otype)) {
270        if ($pclass->can('is_supported')) {
271            return $pclass->is_supported($self);
272        } else {
273            return 1;
274        }
275    } else {
276        return 0;
277    }
278}
279
280=head2 list_objects($otype)
281
282Return the list of UID for object of $otype.
283
284=cut
285
286sub list_objects {
287    my ($self, $otype) = @_;
288    my $pclass = $self->_load_obj_class($otype) or return;
289    $pclass->list($self);
290}
291
292=head2 get_object($type, $id)
293
294Return an object of $type (typically user or group) having identifier
295$id.
296
297=cut
298
299sub get_object {
300    my ($self, $otype, $id) = @_;
301
302    return LATMOS::Accounts::Bases::Objects->_new($self, $otype, $id);
303}
304
305=head2 create_c_object($type, $id, %data)
306
307Create and return an object of type $type with unique id
308$id having %data using canonical fields
309
310=cut
311
312sub create_c_object {
313    my ($self, $otype, $id, %cdata) = @_;
314    $self->check_acl($otype, '@CREATE', 'w') or do {
315        $self->log(LA_WARN, 'permission denied to create object type %s',
316            $otype);
317        return;
318    };
319
320    $self->_create_c_object($otype, $id, %cdata);
321}
322
323sub _create_c_object {
324    my ($self, $otype, $id, %cdata) = @_;
325
326    $id ||= ''; # Avoid undef
327
328    if (my $chk = (
329        lc($otype) eq 'user' || lc($otype) eq 'group')
330        ? LATMOS::Accounts::Utils::check_ug_validity($id)
331        : LATMOS::Accounts::Utils::check_oid_validity($id)) {
332        $self->log(LA_ERR, "Cannot create $otype with ID $id `%s:'", $chk);
333        return;
334    }
335    foreach my $cfield (keys %cdata) {
336        $self->check_allowed_values($otype, $cfield, $cdata{$cfield}) or do {
337            $self->log(LA_ERR, "Cannot create $otype, wrong value");
338            return;
339        };
340    }
341
342    # populating default value
343    {
344        my %default = $self->compute_default($otype, $id, %cdata);
345        foreach my $k (keys %default) {
346            $cdata{$k} = $default{$k};
347        }
348    }
349
350    my %data;
351    foreach my $cfield (keys %cdata) {
352        my $attribute = $self->attribute($otype, $cfield) or next;
353        $attribute->ro and next;
354        $data{$attribute->iname} = $cdata{$cfield};
355    }
356    $self->create_object($otype, $id, %data) or return;
357    my $obj = $self->get_object($otype, $id) or return;
358    $obj->ReportChange('Create', 'Object created with %s', join(', ', sort keys %cdata));
359
360    foreach my $attrname (keys %data) {
361        my $attribute = $self->attribute($obj->type, $attrname) or next;
362        $attribute->monitored or next;
363
364        $obj->ReportChange('Attributes', '%s set to %s', $attrname,
365            (ref $data{$attrname}
366                ? join(', ', @{ $data{$attrname} })
367                : $data{$attrname}) || '(none)');
368    }
369
370    $obj
371}
372
373=head2 create_object($type, $id, %data)
374
375Create and return an object of type $type with unique id
376$id having %data.
377
378This method should be provided by the data base handler.
379
380=cut
381
382sub create_object {
383    my ($self, $otype, $id, %data) = @_;
384    "$id" or do {
385        $self->log(LA_ERR, "Cannot create %s object with empty id",
386            $otype);
387        return;
388    };
389    my $pclass = $self->_load_obj_class($otype) or do {
390        $self->log(LA_ERR, "Cannot create %s object type (cannot load class)",
391            $otype);
392        return;
393    };
394    if ($pclass->_create($self, $id, %data)) {
395        la_log(LA_INFO,
396            'Object %s (%s) created in base %s (%s)',
397            $id, $otype, $self->label, $self->type
398        );
399    } else {
400        la_log(LA_ERR,
401            'Object creation %s (%s) in base %s (%s) failed',
402            $id, $otype, $self->label, $self->type
403        );
404        return;
405    };
406    $self->get_object($otype, $id);
407}
408
409
410=head2 compute_default($otype, $id, %cdata)
411
412Return a hash containing value to set for new object
413
414=cut
415
416sub compute_default {
417    my ($self, $otype, $id, %cdata) = @_;
418
419    my %default;
420    foreach my $def (keys %{ $self->{defattr} || {}}) {
421        if ($def =~ /^$otype\.(.*)$/) {
422            $default{$1} = $self->{defattr}{$def} if(!$cdata{$1});
423        }
424    }
425
426    # computed default value (not a simple set)
427    if (lc($otype) eq 'user') {
428        if (!$cdata{homeDirectory}) {
429            $default{homeDirectory} = $self->{defattr}{'user.homebase'}
430                ? $self->{defattr}{'user.homebase'} . "/$id" 
431                : '';
432        }
433
434        if (!$cdata{uidNumber}) {
435            $default{uidNumber} ||= $self->find_next_numeric_id('user', 'uidNumber',
436            $self->{defattr}{'user.min_uid'}, $self->{defattr}{'user.max_uid'});
437        }
438
439        my $mailid = $cdata{givenName} && $cdata{sn}
440            ? sprintf('%s.%s',
441                to_ascii(lc($cdata{givenName})),
442                to_ascii(lc($cdata{sn})),)
443            : undef;
444        $mailid =~ s/\s+/-/g if($mailid);
445
446        if ($mailid &&
447            $self->is_supported_object('aliases') &&
448            ! $self->get_object('aliases', $mailid)) {
449            if (my $attr = $self->attribute($otype, 'mail')) {
450                if ((!$attr->ro) && $self->{defattr}{'user.maildomain'}) {
451                    $default{mail} ||= sprintf('%s@%s',
452                    $mailid,
453                    $self->{defattr}{'user.maildomain'});
454                }
455            }
456            if (my $attr = $self->attribute($otype, 'aliases')) {
457                $default{aliases} ||= $mailid unless ($attr->ro);
458            }
459            if (my $attr = $self->attribute($otype, 'revaliases')) {
460                $default{revaliases} ||= $mailid unless ($attr->ro);
461            }
462        }
463    } elsif (lc($otype) eq 'group') {
464        if (!$cdata{gidNumber}) {
465            $default{gidNumber} ||= $self->find_next_numeric_id(
466                'group', 'gidNumber',
467                $self->{defattr}{'group.min_gid'},
468                $self->{defattr}{'group.max_gid'}
469            );
470        }
471    }
472
473    return %default;
474}
475
476sub _allowed_values {
477    $_[0]->{_allowed_values}
478}
479
480=head2 obj_attr_allowed_values ($otype, $attr)
481
482Return value allowed for this attribute
483
484=cut
485
486sub obj_attr_allowed_values {
487    my ($self, $otype, $attr) = @_;
488    if ($self->_allowed_values &&
489        $self->_allowed_values->SectionExists("$otype.$attr")) {
490        return grep { defined($_) } $self->_allowed_values->val("$otype.$attr", 'allowed');
491    }
492    return();
493}
494
495=head2 check_allowed_values ($otype, $attr, $attrvalues)
496
497Check attributes C<$attr> of object type C<$otype> allow values C<$attrvalues>
498
499=cut
500
501sub check_allowed_values {
502    my ($self, $otype, $attr, $attrvalues) = @_;
503    $self->_allowed_values or return 1;
504    my @values = ref $attrvalues ? @{ $attrvalues } : $attrvalues;
505    foreach my $value (@values) {
506        $value or next;
507        if (my @allowed = $self->obj_attr_allowed_values($otype, $attr)) {
508            grep { $value eq $_ } @allowed or do {
509                $self->log(LA_ERR,
510                    "value `%s' is not allow for %s.%s per configuration (allowed_values)",
511                    $value, $otype, $attr
512                );
513                return;
514            };
515        }
516    }
517    return 1;
518}
519
520=head2 list_canonical_fields($otype, $for)
521
522Return the list of supported fields by the database for object type $otype.
523
524Optionnal $for specify the goal for which the list is requested, only supported
525fields will be returns
526
527=cut
528
529sub list_canonical_fields {
530    my ($self, $otype, $for) = @_;
531    $for ||= 'rw';
532    my $pclass = $self->_load_obj_class($otype) or return;
533    sort $pclass->_canonical_fields($self, $for);
534}
535
536sub _get_attr_schema {
537    my ($self, $otype) = @_;
538    my $pclass = $self->_load_obj_class($otype) or return;
539    return $pclass->_get_attr_schema($self);
540}
541
542=head2 get_attr_schema
543
544Deprecated
545
546=cut
547
548# TODO: kill this
549
550sub get_attr_schema {
551    my ($self, $otype, $attribute) = @_;
552    my $info = $self->_get_attr_schema($otype);
553    if ($info->{$attribute}) {
554        return $info->{$attribute};
555    } else {
556        return;
557    }
558}
559
560=head2 attribute($otype, $attribute)
561
562Return attribute object.
563
564See L<LATMOS::Accounts::Bases::Attribute>
565
566=cut
567
568sub attribute {
569    my ($self, $otype, $attribute) = @_;
570
571    my $attrinfo;
572    if (!ref $attribute) {
573       $attrinfo = $self->get_attr_schema($otype, $attribute)
574            or return;
575        $attrinfo->{name} = $attribute;
576    } else {
577        $attrinfo = $attribute;
578    }
579
580    return LATMOS::Accounts::Bases::Attributes->new(
581        $attrinfo,
582        $self,
583        $otype,
584    );
585}
586
587=head2 delayed_fields
588
589DEPRECATED
590
591=cut 
592
593# TODO: kill this
594
595sub delayed_fields {
596    my ($self, $otype, $for) = @_;
597    $self->log(LA_WARN, "calling DEPRECATED delayed_fields " . join(',',
598            caller));
599    $for ||= 'rw';
600    my @attrs;
601    foreach ($self->list_canonical_fields($otype, $for)) {
602        my $attr = $self->attribute($otype, $_) or next;
603        $for =~ /w/ && $attr->ro and next;
604        $attr->delayed or next;
605        push(@attrs, $_);
606    }
607    @attrs
608}
609
610=head2 ochelper ($otype)
611
612Return L<LATMOS::Accounts::Bases::OChelper> object
613
614=cut
615
616sub ochelper {
617    my ($self, $otype) = @_;
618    my $pclass = ucfirst(lc($otype));
619    foreach my $class (
620        ref($self) . '::OCHelper::' . $pclass,
621        ref($self) . '::OCHelper',
622        "LATMOS::Accounts::Bases::OCHelper::$pclass",
623        'LATMOS::Accounts::Bases::OCHelper' ) {
624        eval "require $class;";
625        if ($@) { next } # error message ?
626        my $ochelper = "$class"->new($self, $otype);
627        return $ochelper;
628    }
629    return;
630}
631
632=head2 delete_object($otype, $id)
633
634Destroy from data base object type $otype having id $id.
635
636=cut
637
638sub delete_object {
639    my ($self, $otype, $id) = @_;
640    my $obj = $self->get_object($otype, $id) or do {
641        $self->log(LA_WARN, 'Cannot delete %s/%s: no such object',
642            $otype, $id);
643        return;
644    };
645    $self->check_acl($obj, '@DELETE', 'w') or do {
646        $self->log(LA_WARN, 'permission denied to delete %s/%s',
647            $otype, $id);
648        return;
649    };
650    my $ref = $obj->Iid;
651    if (my $res = $self->_delete_object($otype, $id)) {
652        $self->ReportChange($otype, $id, $ref, 'Delete', 'Object deleted');
653        return $res;
654    }
655    return;
656}
657
658sub _delete_object {
659    my ($self, $otype, $id) = @_;
660    my $pclass = $self->_load_obj_class($otype);
661    $pclass->_delete($self, $id);
662}
663
664=head2 rename_object($otype, $id, $newid)
665
666Rename an object.
667
668=cut
669
670sub rename_object {
671    my ($self, $otype, $id, $newid) = @_;
672
673    my $obj = $self->get_object($otype, $id) or do {
674        $self->log(LA_WARN, 'Cannot rename %s/%s: no such object',
675            $otype, $id);
676        return;
677    };
678    if (my $chk = (lc($otype) eq 'user' || lc($otype) eq 'group')
679        ? LATMOS::Accounts::Utils::check_ug_validity($newid)
680        : LATMOS::Accounts::Utils::check_oid_validity($newid)) {
681        $self->log(LA_ERR, "Cannot rename $otype/$id to ID $newid `%s:'", $chk);
682        return;
683    }
684    $self->check_acl($obj, '@DELETE', 'w') &&
685    $self->check_acl($obj, '@CREATE', 'w') or do {
686        $self->log(LA_WARN, 'permission denied to rename %s/%s',
687            $otype, $id);
688        return;
689    };
690
691    my $oldref = $obj->Iid;
692
693    if (my $res = $self->_rename_object($otype, $id, $newid)) {
694        my $newobj = $self->get_object($otype, $newid) or do {
695            $self->log(LA_WARN, 'Cannot get object %s/%s: rename failed ?',
696                $otype, $id);
697            return;
698        };
699
700        $self->ReportChange($otype, $id, $oldref, 'Rename', 'Object rename to %s', $newid);
701        $newobj->ReportChange('Rename', 'Object renamed from %s', $id);
702        return $res;
703    }
704    return;
705}
706
707sub _rename_object {
708    my ($self, $otype, $id, $newid) = @_;
709    my $pclass = $self->_load_obj_class($otype);
710    $pclass->can('_rename') or do {
711        $self->log(LA_ERR, 'rename object type %s is unsupported', $otype);
712        return;
713    };
714    $pclass->_rename($self, $id, $newid);
715}
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 (my $res = $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 ($uobj->_get_c_field('expired')) {
972        la_log(LA_ERR, "Account $username has expired (%s)",
973            $uobj->_get_c_field('expired'));
974        return;
975    }
976
977    if ($uobj->_get_c_field('locked')) {
978        la_log(LA_ERR, "Account $username is currently locked");
979        return;
980    }
981
982    my $password = $uobj->get_field('userPassword') or do {
983        la_log(LA_ERR, "Cannot authenticate user $username having no passwd");
984        return;
985    };
986    if ($password eq crypt($passwd, $password)) { # crypt unix
987        la_log(LA_NOTICE, "User $username authenticated");
988        return 1;
989    } else {
990        la_log(LA_ERR, "Cannot authenticate user $username");
991        return 0;
992    }
993}
994
995=head2 connect($username, $password)
996
997Authenticate the user and store the username as connected
998
999=cut
1000
1001sub connect {
1002    my ($self, $username, $password) = @_;
1003    my $auth = $self->authenticate_user($username, $password);
1004    if ($auth) {
1005        $self->{_user} = $username;
1006        la_log(LA_DEBUG, "Connected as $username");
1007    }
1008    return $auth;
1009}
1010
1011=head2 user
1012
1013Return the current connected username
1014
1015=cut
1016
1017sub user { $_[0]->{_user} }
1018
1019=head2 check_acl($obj, $attr, $perm)
1020
1021Return true if connected user have C<$perm> permission on attribute C<$attr> of
1022object C<$obj>.
1023
1024=cut
1025
1026sub check_acl {
1027    my ($self, $obj, $attr, $perm) = @_;
1028    if ($self->{_acls}) {
1029        my ($who, $groups) = ($self->user || '');
1030        if ($who && (my $uo = $self->get_object('user', $who))) {
1031            $groups = [ $uo->_get_attributes('memberOf') ];
1032        } else {
1033            $who = '';
1034        }
1035        my $res = $self->{_acls}->check($obj, $attr, $perm, $who, $groups);
1036        $self->log(LA_INFO, 'permission denied for "%s" to get %s.%s for %s',
1037           $who, ref $obj ? $obj->id . '(' . $obj->type . ')' : $obj, $attr, $perm) if (!$res);
1038        return $res;
1039    } else {
1040        # No acls, woot
1041        return 1;
1042    }
1043}
1044
1045=head2 text_empty_dump($fh, $otype, $options)
1046
1047Empty object dump
1048
1049=cut
1050
1051sub text_empty_dump {
1052    my ($self, $fh, $otype, $options) = @_;
1053    my $pclass = $self->_load_obj_class($otype) or return;
1054    $pclass->text_dump($fh, $options, $self);
1055}
1056
10571;
1058
1059__END__
1060
1061=head1 SEE ALSO
1062
1063=head1 AUTHOR
1064
1065Thauvin Olivier, E<lt>olivier.thauvin@latmos.ipsl.fr<gt>
1066
1067=head1 COPYRIGHT AND LICENSE
1068
1069Copyright (C) 2009 by Thauvin Olivier
1070
1071This library is free software; you can redistribute it and/or modify
1072it under the same terms as Perl itself, either Perl version 5.10.0 or,
1073at your option, any later version of Perl 5 you may have available.
1074
1075=cut
Note: See TracBrowser for help on using the repository browser.