source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Objects.pm @ 504

Last change on this file since 504 was 504, checked in by nanardon, 15 years ago
  • check login user can chang password of the user
  • Property svn:keywords set to Id Rev
File size: 11.3 KB
Line 
1package LATMOS::Accounts::Bases::Objects;
2
3use 5.010000;
4use strict;
5use warnings;
6use LATMOS::Accounts::Log;
7
8our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
9
10=head1 NAME
11
12LATMOS::Accounts::Bases::Objects - Base class for account objects
13
14=head1 SYNOPSIS
15
16  use LATMOS::Accounts::Bases::Objects;
17  LATMOS::Accounts::Bases::Objects->new($base, $type, $id);
18
19=head1 DESCRIPTION
20
21=head1 FUNCTIONS
22
23=cut
24
25=head2 list($base)
26
27List object supported by this module existing in base $base
28
29Must be provide by object class
30
31    sub list {
32        my ($class, $base) = @_;
33    }
34
35=cut
36
37=head2 new($base, $id)
38
39Create a new object having $id as uid.
40
41=cut
42
43sub new {
44    my ($class, $base, $id, @args) = @_;
45    # So can be call as $class->SUPER::new()
46    bless {
47        _base => $base,
48        _type => lc(($class =~ m/::([^:]*)$/)[0]),
49        _id => $id,
50    }, $class;
51}
52
53# _new($base, $type, $id, ...)
54
55# Return a new object of type $type having unique identifier
56# $id, all remaining arguments are passed to the subclass.
57
58sub _new {
59    my ($class, $base, $otype, $id, @args) = @_;
60
61    # finding perl class:
62    my $pclass = $base->_load_obj_class($otype) or return;
63    my $newobj = "$pclass"->new($base, $id, @args) or return;
64    $newobj->{_base} = $base;
65    $newobj->{_type} = lc($otype);
66    $newobj->{_id} ||= $id;
67    return $newobj;
68}
69
70=head2 _create($class, $base, $id, %data)
71
72Must create a new object in database.
73
74Is called if underling base does not override create_object
75
76    sub _create(
77        my ($class, $base, $id, %data)
78    }
79
80=cut
81
82=head2 type
83
84Return the type of the object
85
86=cut
87
88sub type {
89    my ($self) = @_;
90    if (ref $self) {
91        return $self->{_type}
92    } else {
93        return lc(($self =~ /::([^:]+)$/)[0]);
94    }
95}
96
97=head2 base
98
99Return the base handle for this object.
100
101=cut
102
103sub base {
104    return $_[0]->{_base}
105}
106
107=head2 id
108
109Must return the unique identifier for this object
110
111=cut
112
113sub id {
114    my ($self) = @_;
115    $self->{_id}
116}
117
118=head2 list_canonical_fields($for)
119
120Object shortcut to get the list of field supported by the object.
121
122=cut
123
124sub list_canonical_fields {
125    my ($self, $for) = @_;
126    $self->base->list_canonical_fields($self->type, $for);
127}
128
129=head2 get_field_name($field, $for)
130
131Object shortcut to get the field name supported by the object.
132
133=cut
134
135sub get_field_name {
136    my ($self, $field, $for) = @_;
137    $self->base->get_field_name($self->type, $field, $for);
138}
139
140=head2 _canonical_fields
141
142Must return the list of field supported by the object.
143
144Notice this query will always come from the upstream data base,
145this function is just a facility to store data in the module, but the
146underling database can reply themself.
147
148Is call if underling base doesn't override list_canonical_fields()
149
150See list_canonical_fields().
151
152    sub _canonical_fields {
153        my ($self) = @_;
154    }
155
156=cut
157
158sub _delayed_fields {
159    my ($self)= @_;
160    return ();
161}
162
163=head2 _get_fields_name($field, $for)
164
165Return the fields name for canonical field $field.
166$for, if set, is a string containing 'r' for read, 'w' for write,
167depending usage context.
168
169    sub _get_field_name {
170        my ($self, $field, $for) = @_;
171    }
172
173=cut
174
175=head2 get_field($field)
176
177Return the value for $field, must be provide by data base.
178
179    sub get_field {
180        my ($self, $field)
181    }
182
183=cut
184
185=head2 get_c_field($cfield)
186
187Return the value for canonical field $cfield.
188
189Call driver specific get_field_name() and get_field()
190
191=cut
192
193sub get_c_field {
194    my ($self, $cfield) = @_;
195    my $return;
196    eval {
197        $self->base->check_acl($self, $cfield, 'r') or die "Permission denied";
198        $return = $self->_get_c_field($cfield);
199    };
200    $return
201}
202
203=head2 get_attributes($attr)
204
205Like get_c_field but always return an array
206
207=cut
208
209sub get_attributes {
210    my ($self, $cfield) = @_;
211    my $res = $self->get_c_field($cfield);
212    return ref $res ? @{ $res } : ($res);
213}
214
215sub _get_attributes {
216    my ($self, $cfield) = @_;
217    my $res = $self->_get_c_field($cfield);
218    return ref $res ? @{ $res } : ($res);
219}
220
221sub _get_c_field {
222    my ($self, $cfield) = @_;
223    my $return;
224    eval {
225        my $field = $self->base->get_field_name($self->type, $cfield, 'r') or
226            die "Unknow attribute $cfield";
227        $return = $self->get_field($field);
228    };
229    $return
230}
231
232sub queryformat {
233    my ($self, $fmt) = @_;
234    $fmt =~ s/\\n/\n/g;
235    $fmt =~ s!
236        (?:%{([^:}]*)(?::([^}]+))?})
237        !
238        my $val = $self->get_c_field($1);
239        sprintf('%' . ($2 || 's'), ref $val ? join(',', @$val) : ($val||''))
240        !egx;
241    $fmt;
242}
243
244=head2 set_fields(%data)
245
246Set values for this object. %data is a list or peer field => values.
247
248    sub set_fields {
249        my ($self, %data) = @_;
250    }
251
252=cut
253
254=head2 set_c_fields(%data)
255
256Set values for this object. %data is a list or peer
257canonical field => values. Fields names are translated.
258
259=cut
260
261sub set_c_fields {
262    my ($self, %cdata) = @_;
263    my %data;
264    eval {
265        foreach my $cfield (keys %cdata) {
266            $self->base->check_acl($self, $cfield, 'w') or 
267                die "permission denied";
268        }
269    };
270    return if($@);
271    $self->_set_c_fields(%cdata);
272}
273
274sub _set_c_fields {
275    my ($self, %cdata) = @_;
276    my %data;
277    foreach my $cfield (keys %cdata) {
278        my $field = $self->base->get_field_name($self->type, $cfield, 'w') or do {
279            $self->base->log(LA_ERR,
280                "Cannot set unsupported attribute %s to %s (%s)",
281                $cfield, $self->id, $self->type
282            );
283            return;
284        };
285        $data{$field} = $cdata{$cfield};
286    }
287    keys %data or return 0; # TODO: return an error ?
288    $self->set_fields(%data);
289}
290
291=head2 set_password($password)
292
293Set the password into the database, $password is the clear version
294of the password.
295
296This function store it into userPassword canonical field if supported
297using crypt unix and md5 algorythm (crypt md5), the salt is 8 random
298caracters.
299
300The base driver should override it if another encryption is need.
301
302=cut
303
304sub set_password {
305    my ($self, $clear_pass) = @_;
306    if ($self->base->check_acl($self, 'userPassword', 'w')) {
307        return $self->_set_password($clear_pass);
308    } else {
309        $self->log(LA_ERROR, "Permission denied for %s to change its password",
310            $self->id);
311        return;
312    }
313}
314
315sub _set_password {
316    my ($self, $clear_pass) = @_;
317    if (my $field = $self->base->get_field_name($self->type, 'userPassword')) {
318        my @salt_char = (('a' .. 'z'), ('A' .. 'Z'), (0 .. 9), '/', '.');
319        my $salt = join('', map { $salt_char[rand(scalar(@salt_char))] } (1 .. 8));
320        return $self->set_fields($field, crypt($clear_pass, '$1$' . $salt));
321    } else {
322        $self->log(LA_WARN,
323            "Cannot set password: userPassword attributes is unsupported");
324    }
325}
326
327sub search {
328    my ($class, $base, @filter) = @_;
329    my @results;
330    my @parsed_filter;
331    while (my $item = shift(@filter)) {
332        # attr=foo => no extra white space !
333        # \W is false, it is possible to have two char
334        my ($attr, $mode, $val) = $item =~ /^(\w+)(?:(\W)(.+))?$/ or next;
335        if (!$mode) {
336            $mode = '~';
337            $val = shift(@filter);
338        }
339        push(
340            @parsed_filter,
341            {
342                attr => $attr,
343                mode => $mode,
344                val  => $val,
345            }
346        );
347    }
348    foreach my $id ($base->list_objects($class->type)) {
349        my $obj = $base->get_object($class->type, $id);
350        my $match = 1;
351        foreach my $field (@parsed_filter) {
352            my $value = $field->{val};
353            $base->get_field_name($class->type, $field->{attr}, 'r') or die
354            "Unsupported attribute $field->{attr}\n";
355            my $fval = $obj->_get_c_field($field->{attr}) || '';
356            if ($value eq '*') {
357                if ($fval eq '') {
358                    $match = 0;
359                    last;
360                }
361            } elsif ($value eq '!') {
362                if ($fval ne '') {
363                    $match = 0;
364                    last;
365                }
366            } elsif ($field->{mode} eq '=') {
367                if ($fval ne $value) {
368                    $match = 0;
369                    last;
370                }
371            } elsif($field->{mode} eq '~') {
372                if ($fval !~ m/\Q$value\E/i) {
373                    $match = 0;
374                    last;
375                }
376            }
377        }
378        push(@results, $id) if($match);
379    }
380    @results;
381}
382
383
384sub attributes_summary {
385    my ($class, $base, $attribute) = @_;
386    my %values;
387    foreach my $id ($base->list_objects($class->type)) {
388        my $obj = $base->get_object($class->type, $id);
389        my $value = $obj->_get_c_field($attribute);
390        if ($value) {
391            if (ref $value) {
392                foreach (@$value) {
393                    $values{$_} = 1;
394                }
395            } else {
396                $values{$value} = 1;
397            }
398        }
399    }
400    return sort(keys %values);
401}
402
403sub find_next_numeric_id {
404    my ($class, $base, $field, $min, $max) = @_;
405    $base->get_field_name($class->type, $field) or return;
406    $min ||= 
407        $field eq 'uidNumber' ? 500 :
408        $field eq 'gidNumber' ? 500 :
409        1;
410    $max ||= 65635;
411    $base->log(LA_DEBUG, "Trying to find %s in range %d - %d",
412        $field, $min, $max);
413    my %existsid;
414    foreach ($base->list_objects($class->type)) {
415        my $obj = $base->get_object($class->type, $_) or next;
416        my $id = $obj->_get_c_field($field) or next;
417        $existsid{$id} = 1;
418    }
419    for(my $i = $min; $i <= $max; $i++) {
420        $existsid{$i} or return $i;
421    }
422    return;
423}
424
425sub text_dump {
426    my ($self, $handle, $options, $base) = @_;
427
428    my $otype = $self->type;
429    $base ||= $self->base;
430    if (ref $self) {
431        printf $handle "# base %s: object %s/%s\n",
432            $base->label, $self->type, $self->id;
433    }
434    printf $handle "# %s\n", scalar(localtime);
435
436    foreach my $attr (sort { $a cmp $b } $base->list_canonical_fields($otype,
437        $options->{only_rw} ? 'rw' : 'r')) {
438        my $wok = $base->get_field_name($otype, $attr, 'w');
439        if (ref $self) {
440            my $val = $self->get_c_field($attr);
441            if ($val || $options->{empty_attr}) {
442            my @vals = ref $val ? @{ $val } : $val;
443                foreach (@vals) {
444                    $_ ||= '';
445                    s/\r?\n/\\n/g;
446                    printf($handle "%s%s: %s\n", 
447                        $wok ? '' : '# (ro) ',
448                        $attr, $_);
449                }
450            }
451        } else {
452            printf($handle "%s%s: %s\n", 
453                $wok ? '' : '# (ro) ',
454                $attr, '');
455        }
456    }
457    return 1;
458}
459
4601;
461
462__END__
463
464=head1 CANICALS FIELDS
465
466=head2 User class
467
468=head2 Group class
469
470=head1 SEE ALSO
471
472Mention other useful documentation such as the documentation of
473related modules or operating system documentation (such as man pages
474in UNIX), or any relevant external documentation such as RFCs or
475standards.
476
477If you have a mailing list set up for your module, mention it here.
478
479If you have a web site set up for your module, mention it here.
480
481=head1 AUTHOR
482
483Thauvin Olivier, E<lt>olivier.thauvin.ipsl.fr@localdomainE<gt>
484
485=head1 COPYRIGHT AND LICENSE
486
487Copyright (C) 2009 by Thauvin Olivier
488
489This library is free software; you can redistribute it and/or modify
490it under the same terms as Perl itself, either Perl version 5.10.0 or,
491at your option, any later version of Perl 5 you may have available.
492
493=cut
Note: See TracBrowser for help on using the repository browser.