source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Ldap/User.pm @ 2491

Last change on this file since 2491 was 2491, checked in by nanardon, 3 years ago

Typo

File size: 9.8 KB
Line 
1package LATMOS::Accounts::Bases::Ldap::User;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases::Ldap::objects);
8use Net::LDAP;
9use Net::LDAPS;
10use Net::LDAP::Entry;
11use Net::LDAP::Control::Paged;
12use Net::LDAP::Constant qw( LDAP_CONTROL_PAGED ); 
13use Net::LDAP::Util     qw( escape_filter_value );
14use LATMOS::Accounts::Log;
15use LATMOS::Accounts::Bases::Ldap::Group;
16use LATMOS::Accounts::Utils;
17use Crypt::SmbHash;
18
19our $VERSION = (q$Rev: 649 $ =~ /^Rev: (\d+) /)[0];
20
21=head1 NAME
22
23LATMOS::Ldap - Perl extension for blah blah blah
24
25=head1 SYNOPSIS
26
27  use LATMOS::Ldap;
28  blah blah blah
29
30=head1 DESCRIPTION
31
32Stub documentation for LATMOS::Ldap, created by h2xs. It looks like the
33author of the extension was negligent enough to leave the stub
34unedited.
35
36Blah blah blah.
37
38=head1 FUNCTIONS
39
40=cut
41
42sub _class_filter { '(&(ObjectClass=posixAccount))' }
43
44sub _key_attr { 'cn' } 
45
46sub _my_ldap_classes { qw(
47    top
48    inetOrgPerson
49    organizationalPerson
50    posixAccount
51    shadowAccount
52    sambaSamAccount
53) }
54
55sub _computeSSID {
56    my ($value) = @_;
57    $value * 2 + 1000
58}
59
60sub _get_attr_schema {
61    my ($class, $base) = @_;
62    $class->SUPER::_get_attr_schema($base,
63    {
64        displayName => { },
65        givenName => { },
66        homePhone => { },
67        homePostalAddress => { },
68        initials => { },
69        mail => { },
70        sn => { },
71        mobile => { },
72        o => { },
73        uid => { mandatory => 1 },
74        manager => {
75            delayed => 1,
76            can_values => sub { $base->list_objects('user') },
77        },
78        facsimileTelephoneNumber => { },
79        uidNumber => {
80            uniq => 1,
81            post => sub {
82                my ($self, $value) = @_;
83                $self->object->set_fields(sambaSID => $self->object->base->sambaSID(_computeSSID($value)));
84            },
85        },
86        gidNumber => {
87            reference => 'group',
88            mandatory => 1,
89            can_values => sub {
90                map { $base->get_object('group',
91                        $_)->get_attributes('gidNumber') }
92                $base->list_objects('group')
93            },
94            display => sub {
95                my ($self, $val) = @_;
96                my ($gr) = $self->base->search_objects('group', "gidNumber=$val")
97                    or return;
98                return $gr;
99            },
100
101        },
102        homeDirectory => { },
103        loginShell => { },
104        gecos => { },
105        description => { },
106        shadowLastChange => { },
107        shadowMin => { },
108        shadowMax => { },
109        shadowWarning => { },
110        shadowInactive => { },
111        shadowExpire => { },
112        shadowFlag => { },
113        street => { },
114        postOfficeBox => { },
115        postalCode => { },
116        postalAddress => { },
117        streetAddress => { },
118        physicalDeliveryOfficeName => { },
119        ou => { },
120        employeeType => {},
121        st => { },
122        l => { },
123        telephoneNumber => { },
124        memberOf => {
125            delayed => 1,
126            multiple => 1,
127            reference => 'group',
128        },
129        cn => { ro => 1, },
130        dn => { ro => 1, },
131        objectClass => { ro => 1, },
132        pwdAccountLockedTime => {},
133        pwdPolicySubentry => {},
134        pwdChangedTime => { ro => 1 },
135        labeledURI => {},
136        userPassword    => { readable => 0, },
137        sambaLMPassword => { readable => 0, },
138        sambaNTPassword => { readable => 0, },
139        sambaSID => {
140            uniq => 1,
141            mandatory => 1,
142        },
143        sambaPwdLastSet => {},
144        sambaPwdCanChange => {},
145        sambaPwdMustChange => {},
146        sambaLogonTime => {},
147        sambaLogoffTime => {},
148        sambaKickoffTime => {},
149        sambaBadPasswordCount => {},
150        sambaBadPasswordTime => {},
151        sambaAcctFlags => {},
152    }
153    );
154}
155
156sub _update_class {
157    my ($self) = @_;
158
159    $self->SUPER::_update_class(
160        sambaSID => $self->base->sambaSID(
161            _computeSSID($self->_get_attributes('uidNumber'))
162        )
163    );
164}
165
166sub _create {
167    my ($class, $base, $id, %data) = @_;
168
169    my $entry = Net::LDAP::Entry->new();
170
171    $entry->dn(join(',',
172        sprintf('%s=%s',
173            $class->_dn_attribute($base),
174            escape_filter_value($id)),
175            $base->object_base_dn($class->type),
176        )
177    );
178    $entry->replace(objectClass =>
179        [ $class->_my_ldap_classes ],);
180    $data{cn} ||= $data{sn} || $id; # cn is mandatory
181    $data{sn} ||= $id; # sn is mandatory
182    $data{uid} ||= $id; # uid is mandatory
183    $data{sambaSID} ||= $base->sambaSID(_computeSSID($data{uidNumber}));
184    $data{homeDirectory} ||= '/dev/null'; # homeDirectory is mandatory
185    $data{$class->_key_attribute($base)} = $id;
186    foreach (keys %data) {
187        $class->_populate_entry($entry,
188            $_, $data{$_}, $base);
189    }
190
191    my $msg = $base->ldap->add($entry);
192    if ($msg->code) {
193        $base->log(LA_ERR, "Cannot create user: %s", $msg->error);
194        return 0;
195    }
196    return 1;
197}
198
199sub _rename {
200    my ($class, $base, $uid, $newuid) = @_;
201
202    $class->SUPER::_rename($base, $uid, $newuid) or return;
203}
204
205sub get_field {
206    my ($self, $field) = @_;
207
208    $field eq 'streetAddress' and $field = 'street';
209    $field eq 'memberOf' and do {
210        my @res;
211        $self->base->_unlimited_search(
212            base => $self->base->object_base_dn('group'),
213            filter => sprintf(
214                '(&(objectClass=posixGroup)(memberUID=%s))',
215                escape_filter_value($self->id),
216            ),
217            callback => sub {
218                my ($mesg, $entry) = @_;
219                ref $entry eq 'Net::LDAP::Entry' or return;
220                push(@res, $entry->get_value(
221                    LATMOS::Accounts::Bases::Ldap::Group->_key_attribute($self->base),
222                ));
223                1;
224            },
225        );
226        return [ sort(@res) ];
227    };
228    $field eq 'manager' and do {
229        my $dn = $self->SUPER::get_field($field) or return;
230        my $manager = $self->base->_get_object_from_dn($dn) or return;
231        return $manager->get_value(__PACKAGE__->_key_attribute($self->base));
232    };
233    $self->SUPER::get_field($field);
234}
235
236sub _populate_entry {
237    my ($self, $entry, $f, $val, $base) = @_;
238    $base ||= $self->base;
239    for ($f) {
240        /^sn$/ and $val ||= $entry->get_value($self->_key_attribute);
241        /^memberOf$/ and do {
242            my %users;
243            $users{$_}{e} = 1 foreach (ref $self
244                ? $self->get_attributes('memberOf')
245                : ());
246            $val = [ $val ] unless(ref $val);
247            $users{$_}{n} = 1 foreach (grep { $_ } @{ $val || []});
248            foreach (keys %users) {
249                $users{$_}{e} && $users{$_}{n} and next;
250                my $group = $base->get_object('group', $_) or next;
251                if ($users{$_}{e}) {
252                    $group->{entry}->delete(memberUID =>
253                        $entry->get_value(
254                            LATMOS::Accounts::Bases::Ldap::User->_key_attribute($base),
255                        )
256                    );
257                } elsif ($users{$_}{n}) {
258                    $group->{entry}->add(memberUID =>
259                        $entry->get_value(
260                            LATMOS::Accounts::Bases::Ldap::User->_key_attribute($base),
261                        )
262                    );
263                } # else {} # can't happen
264                my $mesg = $group->{entry}->update($base->ldap);
265                if ($mesg->code) {
266                    $base->log(LA_ERR, "Cannot set attributes: %s", $mesg->error);
267                    return;
268                }
269            }
270            return 1;
271        };
272        /^manager$/ && $val and do {
273            my $user = $base->get_object('user', $val) or do {
274                $val = undef;
275                next;
276            };
277            $val = $user->get_field('dn');
278            next;
279        };
280        /^gecos$/ and do {
281            $val = to_ascii($val);
282        };
283        /^homeDirectory$/ and $val ||= '/dev/null';
284    }
285    $self->SUPER::_populate_entry($entry, $f, $val, $base);
286}
287
288sub _set_password {
289    my ($self, $clear_pass) = @_;
290
291    # openldap use prefix to identify encryption passwd
292    # {CRYPT} is system dependant, eg use crypt from system
293    # As we run openldap on UNIX, this should not be a problem
294    # as we use perl crypt() which does the same
295    # This code will have to be changed if we use openldap on other UNIX
296    my $crypt = '{CRYPT}' . $self->base->passCrypt($clear_pass);
297
298    my ($lm, $nt) = ntlmgen $clear_pass;
299
300    my $res = $self->set_fields(
301        userPassword    => $crypt,
302        sambaLMPassword => $lm,
303        sambaNTPassword => $nt,
304    );
305
306    my $mesg = $self->{entry}->update($self->base->ldap);
307
308    if ($mesg->code && $mesg->code != 82) {
309        $self->base->log(LA_ERR, "Cannot set attributes: %s", $mesg->error);
310        return;
311    } else {
312        $self->base->log(LA_NOTICE, 'Mot de passe changé pour %s', $self->id);
313        return 1;
314    }
315
316}
317
318=head2 _InjectCryptPasswd($cryptpasswd)
319
320Inject a password encrypted using standard UNIX method.
321
322Works only for unix authentification method inside LDAP
323
324=cut
325
326sub _InjectCryptPasswd {
327    my ($self, $cryptpasswd) = @_;
328
329    my $res = $self->set_fields(
330        userPassword => '{CRYPT}' . $cryptpasswd,
331    );
332
333    if ($res) {
334        $self->base->log(LA_NOTICE, 'Crypted password injected for %s', $self->id);
335        return 1;
336    } else {
337        $self->base->log(LA_ERR, 'Cannot inject crypted password for %s', $self->id);
338        return 0;
339    }
340}
341
3421;
343
344__END__
345
346=head1 SEE ALSO
347
348=head1 AUTHOR
349
350Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
351
352=head1 COPYRIGHT AND LICENSE
353
354Copyright (C) 2008 CNRS SA/CETP/LATMOS
355
356This library is free software; you can redistribute it and/or modify
357it under the same terms as Perl itself, either Perl version 5.10.0 or,
358at your option, any later version of Perl 5 you may have available.
359
360
361=cut
Note: See TracBrowser for help on using the repository browser.