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

Last change on this file since 207 was 207, checked in by nanardon, 15 years ago
  • finish set_password in Ad
  • Property svn:keywords set to Id Rev
File size: 6.6 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) {
206        warn $mesg->error;
207        return;
208    }
209
210    my $userAccountControl = $self->get_field('userAccountControl');
211    # http://msdn.microsoft.com/en-us/library/ms680832(VS.85).aspx
212    $userAccountControl |= 0x00010000; # ADS_UF_DONT_EXPIRE_PASSWD
213                                       # The password for this account will never expire.
214    $userAccountControl |= 0x00000040; # ADS_UF_PASSWD_CANT_CHANGE
215                                       # The user cannot change the password.
216    $self->set_fields(userAccountControl => $userAccountControl);
217}
218
2191;
220
221__END__
222
223=head1 SEE ALSO
224
225=head1 AUTHOR
226
227Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
228
229=head1 COPYRIGHT AND LICENSE
230
231Copyright (C) 2008 CNRS SA/CETP/LATMOS
232
233This library is free software; you can redistribute it and/or modify
234it under the same terms as Perl itself, either Perl version 5.10.0 or,
235at your option, any later version of Perl 5 you may have available.
236
237
238=cut
Note: See TracBrowser for help on using the repository browser.