source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases.pm @ 291

Last change on this file since 291 was 285, checked in by nanardon, 15 years ago
  • typo
  • Property svn:keywords set to Id Rev
File size: 10.9 KB
Line 
1package LATMOS::Accounts::Bases;
2
3use 5.010000;
4use strict;
5use warnings;
6use LATMOS::Accounts::Bases::Objects;
7use LATMOS::Accounts::Log;
8use LATMOS::Accounts::Utils qw(exec_command);
9
10our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
11
12=head1 NAME
13
14LATMOS::Accounts::Bases - Base class for account data bases
15
16=head1 SYNOPSIS
17
18  use LATMOS::Accounts::Bases;
19  my $base = LATMOS::Accounts::Bases->new('type', %options);
20  ...
21
22=head1 DESCRIPTION
23
24This module provide basic functions for various account base
25
26=head1 FUNTIONS
27
28=cut
29
30=head2 new($type, %options)
31
32Return, if success, a new data base account object, $type is
33account base type, %options to setup the base.
34
35=cut
36
37sub new {
38    my ($class, $type, %options) = @_;
39
40    my $pclass = ucfirst(lc($type));
41    eval "require LATMOS::Accounts::Bases::$pclass;";
42    if ($@) { return } # error message ?
43    my $base = "LATMOS::Accounts::Bases::$pclass"->new(%options);
44    $base->{_type} = lc($pclass);
45    $base->{_label} = $options{label};
46    $base->{_options} = { %options };
47    $base->{wexported} = 0;
48    $base->{defattr} = $options{defattr};
49    la_log(LA_DEBUG, 'Instanciate base %s (%s)', ($options{label} || 'N/A'), $pclass);
50    $base
51}
52
53sub wexported {
54    my ($self, $wexported) = @_;
55    my $old = $self->{wexported};
56    if (defined($wexported)) {
57        $self->{wexported} = $wexported;
58        $self->log(LA_DEBUG, "Switching exported mode: %s => %s", $old,
59            $wexported);
60    }
61    return($old || 0);
62}
63
64sub log {
65    my ($self, $level, $msg, @args) = @_;
66    my $prefix = 'Base(' . $self->label . ')';
67    LATMOS::Accounts::Log::la_log($level, "$prefix $msg", @args);
68}
69
70sub label {
71    $_[0]->{_label};
72}
73
74sub type {
75    $_[0]->{_type};
76}
77
78sub _load_obj_class {
79    my ($self, $otype) = @_;
80
81    # finding perl class:
82    my $pclass = ref $self;
83    $pclass .= '::' . ucfirst(lc($otype));
84    eval "require $pclass;";
85    if ($@) {
86        $self->log(LA_DEBUG, 'Cannot load perl class %s', $pclass);
87        return
88    } # error message ?
89    return $pclass;
90}
91
92=head2 list_canonical_fields($otype, $for)
93
94Return the list of supported fields by the database for object type $otype.
95
96Optionnal $for specify the goal for which the list is requested, only supported
97fields will be returns
98
99=cut
100
101sub list_canonical_fields {
102    my ($self, $otype, $for) = @_;
103    $for ||= 'rw';
104    my $pclass = $self->_load_obj_class($otype) or return;
105    sort $pclass->_canonical_fields($self, $for);
106}
107
108sub delayed_fields {
109    my ($self, $otype, $for) = @_;
110    $for ||= 'rw';
111    my $pclass = $self->_load_obj_class($otype) or return;
112    $pclass->_delayed_fields($self, $for);
113}
114
115=head2 get_field_name($otype, $c_fields, $for)
116
117Return the internal fields name for $otype object for
118canonical fields $c_fields
119
120=cut
121
122sub get_field_name {
123    my ($self, $otype, $c_fields, $for) = @_;
124    $for ||= 'rw';
125    my $pclass = $self->_load_obj_class($otype) or return;
126    $pclass->_get_field_name($c_fields, $self, $for);
127}
128
129=head2 list_supported_objects(@otype)
130
131Return a list of supported object
132
133@type is an additionnal list of objects to check
134
135=cut
136
137sub list_supported_objects {
138    my ($self, @otype) = @_;
139    my %res;
140    foreach my $inc (@INC) {
141        my $sub = 'LATMOS::Accounts::Bases::' . ucfirst($self->type);
142        $sub =~ s/::/\//g;
143        foreach (glob("$inc/$sub/[A-Z]*.pm")) {
144            s/.*\///;
145            s/\.pm$//;
146            $res{lc($_)} = 1;
147        }
148    }
149    $res{$_} = 1 foreach(@otype);
150    my @sobj = grep { $self->is_supported_object($_) } keys %res;
151    la_log(LA_DEBUG, "Base %s supported objects: %s", $self->type, join(', ', @sobj));
152    return @sobj;
153}
154
155=head2 is_supported_object($otype)
156
157Return true is object type $otype is supported
158
159=cut
160
161sub is_supported_object {
162    my ($self, $otype) = @_;
163    return $self->_load_obj_class($otype) ? 1 : 0;
164}
165
166=head2 list_objects($otype)
167
168Return the list of UID for object of $otype.
169
170=cut
171
172sub list_objects {
173    my ($self, $otype) = @_;
174    my $pclass = $self->_load_obj_class($otype) or return;
175    $pclass->list($self);
176}
177
178=head2 get_object($type, $id)
179
180Return an object of $type (typically user or group) having identifier
181$id.
182
183=cut
184
185sub get_object {
186    my ($self, $otype, $id) = @_;
187
188    return LATMOS::Accounts::Bases::Objects->_new($self, $otype, $id);
189}
190
191=head2 create_object($type, $id, %data)
192
193Create and return an object of type $type with unique id
194$id having %data.
195
196This method should be provided by the data base handler.
197
198=cut
199
200sub create_object {
201    my ($self, $otype, $id, %data) = @_;
202    my $pclass = $self->_load_obj_class($otype);
203    if ($pclass->_create($self, $id, %data)) {
204        la_log(LA_INFO,
205            'Object %s (%s) created in base %s (%s)',
206            $id, $otype, $self->label, $self->type
207        );
208    } else {
209        la_log(LA_ERR,
210            'Object creation %s (%s) in base %s (%s) failed',
211            $id, $otype, $self->label, $self->type
212        );
213        return;
214    };
215    $self->get_object($otype, $id);
216}
217
218=head2 create_c_object($type, $id, %data)
219
220Create and return an object of type $type with unique id
221$id having %data using canonical fields
222
223=cut
224
225sub create_c_object {
226    my ($self, $otype, $id, %cdata) = @_;
227
228    # populating default value
229    foreach my $def (%{ $self->{defattr} || {}}) {
230        if ($def =~ /^$otype\.(.*)$/) {
231            $cdata{$1} = $self->{defattr}{$def} if(!$cdata{$1});
232        }
233    }
234    if ($otype eq 'user') {
235        $cdata{homeDirectory} ||= $self->{defattr}{'user.homebase'} ?
236            $self->{defattr}{'user.homebase'} . "/$id" : '';
237        $cdata{uidNumber} ||= $self->find_next_numeric_id('user', 'uidNumber',
238            $self->{defattr}{'user.min_uid'}, $self->{defattr}{'user.max_uid'});
239    } elsif ($otype eq 'group') {
240        $cdata{gidNumber} ||= $self->find_next_numeric_id('group', 'gidNumber',
241            $self->{defattr}{'group.min_gid'}, $self->{defattr}{'group.max_gid'});
242    }
243    my %data;
244    foreach my $cfield (keys %cdata) {
245        my $field = $self->get_field_name($otype, $cfield, 'write') or next;
246        $data{$field} = $cdata{$cfield};
247    }
248    keys %data or return 0; # TODO: return an error ?
249    $self->create_object($otype, $id, %data);
250}
251
252=head2 delete_object($otype, $id)
253
254Destroy from data base object type $otype having id $id.
255
256=cut
257
258sub delete_object {
259    my ($self, $otype, $id) = @_;
260    my $pclass = $self->_load_obj_class($otype);
261    $pclass->_delete($self, $id);
262}
263
264=head2 load
265
266Make account base loading data into memory if need.
267Should always be called, if database fetch data on the fly
268(SQL, LDAP), the function just return True.
269
270=cut
271
272sub load { 1 }
273
274=head2 is_transactionnal
275
276Return True is the database support commit and rollback
277
278=cut
279
280sub is_transactionnal {
281    my ($self) = @_;
282    return($self->can('_rollback') && $self->can('_commit'));
283}
284
285=head2 commit
286
287Save change into the database if change are not done immediately.
288This should always be called as you don't know when change are applied.
289
290Return always true if database does not support any transaction.
291
292The driver should provides a _commit functions to save data.
293
294=cut
295
296sub commit {
297    my ($self) = @_;
298    if ($self->can('_commit')) {
299        la_log(LA_DEBUG, 'Commiting data');
300        if (!(my $res = $self->_commit)) {
301            la_log(LA_ERR, "Commit error on %s", $_->label);
302            return $res;
303        }
304    }
305    if ($self->{_options}{postcommit}) {
306        la_log(LA_DEBUG, "Running post commit `%s'", $self->{_options}{postcommit});
307        return exec_command($self->{_options}{postcommit}, $self->{_options});
308    } else {
309        la_log(LA_DEBUG, "No postcommit setting, ignoring this");
310    }
311    return 1;
312}
313
314=head2 rollback
315
316If database support transaction, rollback changes. Return false
317if database does not support.
318
319If supported, driver should provides a _rollback functions
320
321=cut
322
323sub rollback {
324    my ($self) = @_;
325    if ($self->can('_rollback')) {
326       la_log(LA_DEBUG, 'Rolling back data');
327       return $self->_rollback;
328   } else {
329       return 0;
330   }
331}
332
333=head2 current_rev
334
335Return the current revision of the database
336
337Must be provide by base driver if incremental synchro is supported
338
339=cut
340
341sub current_rev { return }
342
343=head2 list_objects_from_rev($otype, $rev)
344
345Return the list of UID for object of $otype.
346
347=cut
348
349sub list_objects_from_rev {
350    my ($self, $otype, $rev) = @_;
351    my $pclass = $self->_load_obj_class($otype) or return;
352    if (defined($rev) && $pclass->can('list_from_rev')) {
353        return $pclass->list_from_rev($self, $rev);
354    } else {
355        # no support, return all objects...
356        return $self->list_objects($otype);
357    }
358}
359
360=head2 sync_object
361
362Synchronise an object into this base
363
364=cut
365
366sub sync_object {
367    my ($self, $srcobj, %options) = @_;
368    $self->is_supported_object($srcobj->type) or return '';
369    my @fields = $options{attrs}
370        ? @{ $options{attrs} }
371        : $self->list_canonical_fields($srcobj->type, 'w');
372    my %data;
373    foreach (@fields) {
374        $srcobj->get_field_name($_, 'r') or next;
375        $data{$_} = $srcobj->get_c_field($_);
376    }
377    if (my $dstobj = $self->get_object($srcobj->type, $srcobj->id)) {
378        return 'SYNCHED' if ($dstobj->set_c_fields(%data));
379    } elsif(!$options{nocreate}) {
380        return 'CREATE' if ($self->create_c_object($srcobj->type, $srcobj->id, %data));
381    } else {
382        # No error, but creation is denied
383        return 'Creation skipped';
384    }
385
386    return;
387}
388
389=head2 search_objects($otype, %filter)
390
391Search object according %filter. %filter is a list
392of field/value which should match.
393
394A default function is provided but each db driver can provide
395an optimize version.
396
397=cut
398
399sub search_objects {
400    my ($self, $otype, %filter) = @_;
401    my $pclass = $self->_load_obj_class($otype) or return;
402    $pclass->search($self, %filter);
403}
404
405sub attributes_summary {
406    my ($self, $otype, $attr) = @_;
407    my $pclass = $self->_load_obj_class($otype) or return;
408    $pclass->attributes_summary($self, $attr);
409}
410
411sub find_next_numeric_id {
412    my ($self, $otype, $field, $min, $max) = @_;
413    my $pclass = $self->_load_obj_class($otype) or return;
414    $pclass->find_next_numeric_id($self, $field, $min, $max);
415}
416
417sub authenticate_user {
418    my ($self, $username, $passwd) = @_;
419    $username or return;
420    my $uobj = $self->get_object('user', $username) or do {
421        la_log(LA_ERR, "Cannot authenticate non existing user $username");
422        return;
423    };
424    my $password = $uobj->get_c_field('userPassword') or do {
425        la_log(LA_ERR, "Cannot authenticate user $username having no passwd");
426        return;
427    };
428    if ($password eq crypt($passwd, $password)) { # crypt unix
429        return 1;
430    } else {
431        la_log(LA_ERR, "Cannot authenticate user $username");
432        return 0;
433    }
434}
435
4361;
437
438__END__
439
440=head1 SEE ALSO
441
442=head1 AUTHOR
443
444Thauvin Olivier, E<lt>olivier.thauvin@latmos.ipsl.fr<gt>
445
446=head1 COPYRIGHT AND LICENSE
447
448Copyright (C) 2009 by Thauvin Olivier
449
450This library is free software; you can redistribute it and/or modify
451it under the same terms as Perl itself, either Perl version 5.10.0 or,
452at your option, any later version of Perl 5 you may have available.
453
454=cut
Note: See TracBrowser for help on using the repository browser.