source: LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Ad/User.pm @ 203

Last change on this file since 203 was 203, checked in by nanardon, 15 years ago
  • fix attributes assigment in AD
  • Property svn:keywords set to Id Rev
File size: 6.2 KB
Line 
1package LATMOS::Accounts::Bases::Ad::User;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases::Ad::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 );
14
15our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
16
17=head1 NAME
18
19LATMOS::Ad - Perl extension for blah blah blah
20
21=head1 SYNOPSIS
22
23  use LATMOS::Ad;
24  blah blah blah
25
26=head1 DESCRIPTION
27
28Stub documentation for LATMOS::Ad, created by h2xs. It looks like the
29author of the extension was negligent enough to leave the stub
30unedited.
31
32Blah blah blah.
33
34=head1 FUNCTIONS
35
36=cut
37
38sub _class_filter { '(&(ObjectClass=user) (!(ObjectClass=computer)))' }
39
40sub _key_attr { 'cn' } 
41
42sub _delayed_fields {
43    my ($self)= @_;
44    return qw(memberOf);
45}
46
47sub _canonical_fields {
48    my ($self, $base, $mode) = @_;
49    (
50        qw(
51        sn name givenName
52        sAMAccountName uid gecos
53        homeDirectory loginShell
54        uidNumber gidNumber gecos
55        shadowLastChange shadowMin shadowMax
56        shadowWarning shadowInactive shadowExpire
57        shadowFlag
58        description
59        mail
60        ipPhone otherTelephone department
61        title modbile homePhone
62        accountExpires
63        streetAddress postalCode postOfficeBox l
64        physicalDeliveryOfficeName
65        company st
66        displayName
67        initials
68        manager
69        userAccountControl
70        locked
71        memberOf
72        ),
73        ($mode !~ /w/
74            ? qw(cn dn uSNCreated uSNChanged directReports)
75            : ()
76        )
77    )
78}
79
80sub _create {
81    my ($class, $base, $id, %data) = @_;
82
83    my $entry = Net::LDAP::Entry->new();
84
85    $entry->dn(join(',',
86        sprintf('cn=%s', escape_filter_value($id)),
87        $base->object_base_dn($class->type),
88    ));
89    $entry->replace('sAMAccountName', $id);
90    $entry->replace(objectClass => [ qw(top person organizationalPerson user)],);
91    # Must be 544 for creation...
92    $entry->replace(userAccountControl => 544); #66112);
93    # This attributes cannot be set via LDAP:
94    #$entry->replace(sAMAccountType => 0x30000000);
95    $entry->replace(accountExpires => '9223372036854775807'); # TODO hardcoded, burk
96    $entry->replace(userPrincipalName => "$id\@" . $base->ad_domain);
97    my %delayed;
98    foreach (keys %data) {
99        /^(manager|memberOf)$/ and do {
100            $delayed{$_} = $data{$_};
101            next;
102        };
103        $class->_populate_entry($entry, $_, $data{$_});
104    }
105    my $msg = $base->ldap->add($entry);
106    if ($msg->code) {
107        warn $msg->error;
108        return 0;
109    }
110    my $res = $base->get_object('user', $id)->set_fields(%delayed);
111    return defined($res) ? 1 : 0;
112}
113
114sub get_field {
115    my ($self, $field) = @_;
116
117    $field eq 'memberOf' and do {
118        my @res;
119        $self->base->_unlimited_search(
120            base => $self->base->object_base_dn('group'),
121            filter => sprintf(
122                '(&(objectClass=group)(member=%s))',
123                escape_filter_value($self->{entry}->dn),
124            ),
125            callback => sub {
126                my ($mesg, $entry) = @_;
127                ref $entry eq 'Net::LDAP::Entry' or return;
128                push(@res, $entry->get_value('cn'));
129            },
130        );
131        return [ sort(@res) ];
132    };
133    $field eq 'directReports' and do {
134        my $res = $self->SUPER::get_field($field) or return;
135        return [
136            map { $self->base->_get_object_from_dn($_)->get_value('cn') }
137            @{ ref $res ? $res : [ $res ] }
138        ];
139    };
140    $field eq 'manager' and do {
141        my $dn = $self->SUPER::get_field($field) or return;
142        return $self->base->_get_object_from_dn($dn)->get_value('cn');
143    };
144    $self->SUPER::get_field($field);
145}
146
147sub set_fields {
148    my ($self, %data) = @_;
149    my %ndata;
150    while (my ($f, $val) = each(%data)) {
151        $f eq 'memberOf' and do {
152            my %users;
153            $users{$_}{e} = 1 foreach (@{ $self->get_field('memberOf') || []});
154            $users{$_}{n} = 1 foreach (@{ $val || []});
155            foreach (keys %users) {
156                $users{$_}{e} && $users{$_}{n} and next;
157                my $group = $self->base->get_object('group', $_) or next;
158                # memberOf field is read only
159                # so we modify the peer entry into peer object
160                if ($users{$_}{e}) {
161                    $group->{entry}->delete(member => $self->get_field('dn'));
162                } elsif ($users{$_}{n}) {
163                    $group->{entry}->add(member => $self->get_field('dn'));
164                } # else {} # can't happen
165                my $mesg = $group->{entry}->update($self->base->ldap);
166                if ($mesg->code) {
167                    warn $mesg->error;
168                    return;
169                }
170            }
171            next;
172        };
173        $f eq 'manager' && $val and do {
174            my $user = $self->base->get_object('user', $val) or next;
175            $ndata{$f} = $user->get_field('dn');
176            next;
177        };
178        $f eq 'locked' and do {
179            my $uac = $self->get_field('userAccountControl');
180            if ($val) {
181                $uac |= 0x00000002;
182            } else {
183                $uac &= (0xFFFFFFFF ^ 0x00000002);
184            }
185            $ndata{userAccountControl} = "$uac";
186            next;
187        };
188        $ndata{$f} = $val;
189    }
190    $self->SUPER::set_fields(%ndata);
191}
192
193sub set_password {
194    my ($self, $clear_pass) = @_;
195    my $charmap = Unicode::Map8->new('latin1')  or  die;
196    my $newUniPW = $charmap->tou('"'.$clear_pass.'"')->byteswap()->utf16();
197    my $mesg = $self->base->ldap->modify(
198        $self->get_c_field('dn'),
199        changes => [
200        #replace => [ userPassword => $clear_pass ],
201        replace => [ unicodePwd => $newUniPW ],
202
203        ]
204    );
205    if ($mesg->code && $mesg->code != 82) {
206        warn $mesg->error;
207        return;
208    } else { return 1 }
209}
210
2111;
212
213__END__
214
215=head1 SEE ALSO
216
217=head1 AUTHOR
218
219Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
220
221=head1 COPYRIGHT AND LICENSE
222
223Copyright (C) 2008 CNRS SA/CETP/LATMOS
224
225This library is free software; you can redistribute it and/or modify
226it under the same terms as Perl itself, either Perl version 5.10.0 or,
227at your option, any later version of Perl 5 you may have available.
228
229
230=cut
Note: See TracBrowser for help on using the repository browser.