source: branches/samba-3.0/examples/LDAP/smbldap-tools-0.9.2/doc/smbldap-migrate-unix-accounts

Last change on this file was 1, checked in by Paul Smedley, 18 years ago

Initial code import

File size: 11.4 KB
Line 
1#!/usr/bin/perl -w
2
3# Created by P.Wieleba@iem.pw.edu.pl in 2004
4
5use strict;
6use Getopt::Std;
7use FindBin;
8use FindBin qw($RealBin);
9use lib "$RealBin/";
10use smbldap_tools;
11
12# function declaration
13sub migrate_user;
14sub migrate_shadow_user;
15sub get_user_entry;
16sub exist_in_tab;
17sub del_from_tab;
18sub add_to_tab;
19sub read_shadow_file;
20
21# smbldap-migrate-unix-accounts (-? or -h for help)
22#
23#
24
25my %Options;
26
27my $ok = getopts('M:P:S:vn?hd:a', \%Options);
28
29if ( (!$ok) || ($Options{'?'}) || ($Options{'h'}) || (!keys(%Options)) ) {
30 print "Usage: $0 [-PSMvn?hda]\n";
31 print " -?|-h show this help message\n";
32 print " -P file import passwd file\n";
33 print " -S file import shadow file\n";
34 print " -M file import FreeBSD master.passwd\n";
35 print " -v displays modified entries to STDOUT\n";
36 print " -n do everything execpt updating LDAP\n";
37 print " -d obj_nam delete and add (not just update) existing entry in LDAP\n";
38 print " -a adds sambaSamAccount objectClass\n";
39 exit (1);
40}
41
42my $INFILE = undef;
43my %shadowUsers;
44
45if ( $Options{'M'} ) {
46 open($INFILE,$Options{'M'}) or
47 die "I cannot open file: " . $Options{'M'} . "\n";
48} elsif ( $Options{'P'} ) {
49 open($INFILE,$Options{'P'}) or
50 die "I cannot open file: " . $Options{'P'} . "\n";
51 # if defined -S option also read shadow file
52 if ( $Options{'S'} ) {
53 %shadowUsers = read_shadow_file($Options{'S'});
54 (%shadowUsers) or ( close($INFILE) and
55 die "I cannot open file: " . $Options{'S'} . "\n" );
56 }
57} elsif ( $Options{'S'} ) {
58 open($INFILE,$Options{'S'}) or
59 die "I cannot open file: " . $Options{'S'} . "\n";
60}
61
62my $ldap_master=connect_ldap_master();
63
64while ( my $line=<$INFILE> ) {
65 chop($line);
66 next if ( $line =~ /^\s*$/ ); # whitespace
67 next if ( $line =~ /^#/ );
68 next if ( $line =~ /^\+/ );
69 my $entry = undef;
70 if ($Options{'M'}) {
71 my($user,$pwd,$uid,$gid,$class,$change,$expire,$gecos,$homedir,$shell) = split(/:/,$line);
72 # if user is not in LDAP new entry will be created
73 $entry = get_user_entry($ldap_master,$user);
74 $entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell);
75 # for master.passwd file (nss_ldap)
76 if ($entry) {
77 my @objectClass = $entry->get_value( 'objectClass' );
78 $entry->replace( 'objectClass' => [add_to_tab(\@objectClass,'shadowAccount')] );
79 }
80 } elsif ($Options{'P'}) {
81 my($user,$pwd,$uid,$gid,$gecos,$homedir,$shell) = split(/:/,$line);
82 # if user is not in LDAP new entry will be created
83 $entry = get_user_entry($ldap_master,$user);
84 $entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell,undef);
85
86 # should I delete next functionality
87 # add shadow entries if also -S defined
88 if ($Options{'S'} and $shadowUsers{$user}) {
89 my($user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag) = split(/:/,$shadowUsers{$user});
90 $entry = migrate_shadow_user($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag);
91 }
92 } elsif ($Options{'S'}) {
93 my($user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag)=split(/:/,$line);
94 # if user is not in LDAP new entry will be created
95 $entry = get_user_entry($ldap_master,$user);
96 $entry = migrate_shadow_user($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag);
97 }
98
99 if ($entry) {
100 # objectClass $Options{'d'} will be removed
101 # from entry if it exists
102 if ($Options{'d'}) {
103 my @objectClass = $entry->get_value( 'objectClass' );
104 $entry->replace( 'objectClass' => [del_from_tab(\@objectClass,$Options{'d'})] );
105 #$entry->delete( 'objectClass' => [ $Options{'d'} ] );
106 }
107 # if used "-a" and sambaSamAccount doesn't exist.
108 if ( $Options{'a'} and !exist_in_tab([$entry->get_value('objectClass')],'sambaSamAccount') ) {
109 my @objectClass = $entry->get_value( 'objectClass' );
110 $entry->replace( 'objectclass' => [add_to_tab(\@objectClass,'sambaSamAccount')] );
111
112 # the below part comes from smbldap-useradd and
113 # maybe it should be replaced by a new subroutine.
114 my $userUidNumber = $entry->get_value('uidNumber');
115 # as rid we use 2 * uid + 1000
116 my $userRid = 2 * $userUidNumber + 1000;
117 # let's test if this SID already exist
118 my $user_sid = "$config{SID}-$userRid";
119 my $test_exist_sid = does_sid_exist($user_sid,$config{usersdn});
120 if ($test_exist_sid->count == 1) {
121 print "User SID already owned by\n";
122 # there should not exist more than one entry, but ...
123 foreach my $entry ($test_exist_sid->all_entries) {
124 my $dn= $entry->dn;
125 chomp($dn);
126 print "$dn\n";
127 }
128 } else {
129 $entry->replace( 'sambaSID' => $user_sid );
130 }
131 }
132 if ($Options{'v'}) {
133 $entry->dump();
134 }
135 if (!$Options{'n'}) {
136 my $mesg;
137 if ( $Options{'d'} ) {
138 # delete entry from LDAP if it exists
139 $mesg = $ldap_master->search( base => $entry->dn(),
140 scope => 'sub',
141 filter => '(objectClass=*)'
142 );
143 if ( $mesg->count() == 1 ) {
144 $mesg = $ldap_master->delete($entry->dn());
145 if ($mesg->is_error()) {
146 print "Error: " . $mesg->error() . "\n";
147 }
148 $entry->changetype('add');
149 }
150 }
151 $mesg = $entry->update($ldap_master);
152 if ($mesg->is_error()) {
153 print "Error: " . $mesg->error() . "\n";
154 }
155 }
156 }
157}
158
159$INFILE and close($INFILE);
160# take down the session
161$ldap_master and $ldap_master->unbind;
162
163# returns updated $entry
164sub migrate_user
165 {
166 my($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell) = @_;
167 my($name,$office,$wphone,$hphone)=split(/,/,$gecos);
168 my($cn);
169
170 # posixAccount MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
171 my @objectClass = $entry->get_value( 'objectClass' );
172 @objectClass = add_to_tab(\@objectClass,'posixAccount');
173 @objectClass = add_to_tab(\@objectClass,'inetOrgPerson');
174 $entry->replace( 'objectClass' => \@objectClass );
175
176 $entry->replace( 'uid' => $user );
177 if ($name) {
178 $cn = $name;
179 } else {
180 $cn = $user;
181 }
182 $entry->replace( 'cn' => $cn );
183 # perhaps I should delete it
184 if ( exist_in_tab(\@objectClass,'inetOrgPerson') ) {
185 # 'sn' is required by person objectClass from core.schema
186 my @tmp = split(/\s+/,$cn);
187 my $sn = $tmp[$#tmp];
188 $entry->replace( 'sn' => $sn );
189 # perhaps 'telephoneNumber' 'roomNumber' 'homePhone'
190 # and 'givenName' also should be modified ???????
191 }
192 ($pwd) and $entry->replace( 'userPassword' => "{crypt}" . $pwd );
193 ($uid ne "") and $entry->replace( 'uidNumber' => $uid );
194 ($gid ne "") and $entry->replace( 'gidNumber' => $gid );
195 ($gecos) and $entry->replace( 'gecos' => $gecos );
196 ($homedir) and $entry->replace( 'homeDirectory' => $homedir );
197 ($shell) and $entry->replace( 'loginShell' => $shell );
198
199 return $entry;
200 }
201
202# returns updated $entry
203sub migrate_shadow_user
204 {
205 my($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag) = @_;
206
207 # shadowAccount MUST uid
208 my @objectClass = $entry->get_value( 'objectClass' );
209 # if the entry doesn't exist, it needs structural objectclass
210 (@objectClass) or push(@objectClass,'account');
211 $entry->replace( 'objectClass' => [add_to_tab(\@objectClass,'shadowAccount')] );
212
213 $entry->replace( 'uid' => $user );
214 ($pwd) and $entry->replace( 'userPassword' => "{crypt}" . $pwd );
215 ($lastchg) and $entry->replace( 'shadowLastChange' => $lastchg );
216 ($min) and $entry->replace( 'shadowMin' => $min );
217 ($max) and $entry->replace( 'shadowMax' => $max );
218 ($warn) and $entry->replace( 'shadowWarning' => $warn );
219 ($inactive) and $entry->replace( 'shadowInactive' => $inactive );
220 ($expire) and $entry->replace( 'shadowExpire' => $expire );
221 ($flag) and $entry->replace( 'shadowFlag' => $flag );
222
223 return $entry;
224 }
225
226# creates a _new_entry_ if user doesn't exist in ldap
227# else return's ldap user entry
228sub get_user_entry
229 {
230 my($ldap_master,$user) = @_;
231
232 # do not use read_user_entry()
233 my $mesg = $ldap_master->search( base => $config{usersdn},
234 scope => 'one',
235 filter => "(uid=$user)"
236 );
237 my $entry;
238 if ( $mesg->count() != 1 ) {
239 $entry = Net::LDAP::Entry->new();
240 $entry->dn("uid=$user,$config{usersdn}");
241 } else {
242 $entry = $mesg->entry(0); # ????
243 }
244 return $entry;
245 }
246
247# Check if a $text element exists in @table
248# eg. exist_in_tab(\@table,$text);
249sub exist_in_tab
250 {
251 my($ref_tab,$text) = @_;
252 my @tab = @$ref_tab;
253
254 foreach my $elem (@tab) {
255 if ( lc($elem) eq lc($text) ) {
256 return 1;
257 }
258 }
259 return 0;
260 }
261
262# Delete $text element from @table
263# eg. del_from_tab(\@table,$text);
264sub del_from_tab
265 {
266 my($ref_tab,$text) = @_;
267 my @tab = @$ref_tab;
268 my @new_tab;
269
270 foreach my $elem (@tab) {
271 if ( lc($elem) ne lc($text) ) {
272 push(@new_tab,$elem);
273 }
274 }
275 return @new_tab;
276 }
277
278# Add $text to tab if it doesn't exist there
279sub add_to_tab
280 {
281 my($ref_tab,$text) = @_;
282 my @tab = @$ref_tab;
283
284 if ( !exist_in_tab(\@tab,$text) ) {
285 push(@tab,$text);
286 }
287 return @tab;
288 }
289
290# reads shadow file entries and places them in a hash
291sub read_shadow_file
292 {
293 my($shadow) = @_;
294
295 my $shadowUser;
296 my %shadowUsers;
297 open(SHADOW,$shadow) or
298 return ;
299 while (my $line=<SHADOW>) {
300 chop($line);
301 next if ( $line =~ /^\s*$/ ); # whitespace
302 next if ( $line =~ /^#/ );
303 ($shadowUser) = split(/:/, $line);
304 $shadowUsers{$shadowUser} = $line;
305 }
306 close(SHADOW);
307 return %shadowUsers;
308 }
309
310########################################
311
312=head1 NAME
313
314smbldap-migrate-unix-accounts - Migrate unix accounts to LDAP
315
316=head1 SYNOPSIS
317
318smbldap-migrate-unix-accounts [-P file] [-S file] [-M file] [-n] [-v]
319[-h] [-?] [-d]
320
321=head1 DESCRIPTION
322
323This command processes one file as defined by option and
324creates new or changes existing ldap user entry.
325New attributes are added, and existing are changed.
326None of the existing attributes is deleted.
327
328-P passwd_file
329 Processes passwd_file and uptades LDAP. Creates new ldap user
330 entry or just adds posixAccount objectclass and corresponding
331 attributes to the ldap user entry or just uptades their values.
332
333-S shadow_file
334 Reads shadow_file and uptades LDAP. Creates new ldap user
335 entry or just adds shadowAccount objectclass and corresponding
336 attributes to the ldap user entry or just uptades their values.
337
338-M master.passwd_file
339 Reads master.passwd_file and uptades LDAP. Creates new ldap user
340 entry or just adds shadowAccount and posixAccount objectclass
341 and corresponding attributes to the ldap user entry or just
342 uptades their values.
343
344-h show the help message
345
346-? the same as -h
347
348-v displayes modified entries to STDOUT
349
350-n do everything execpt updating LDAP. It is useful when used
351 with -v switch.
352
353-d objeClass_name
354 In spite of just updating existing user entry, the entry will be
355 deleted from LDAP and a new one will be added.
356 It is essential to use this option if you update users in LDAP
357 and want to change their structural objectClass.
358 Use it in the example schema:
359 There are no users in LDAP, and you migrate samba first.
360 # pdbedit -e ldapsam:ldap://localhost
361 # smbldap-migrate-passwd -P passwd -d 'account'
362
363-a adds sambaSamAccount objectClass and generates sambaSID attribute
364
365=cut
366
367#'
368
369# The End
370
371
Note: See TracBrowser for help on using the repository browser.