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

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