4 # Copyright (c) 1999-2000 Jason Gunthorpe <jgg@debian.org>
5 # Copyright (c) 2001-2003 James Troup <troup@debian.org>
6 # Copyright (c) 2004 Joey Schulze <joey@infodrom.org>
7 # Copyright (c) 2008,2009,2010 Peter Palfrader <peter@palfrader.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 import re, time, ldap, getopt, sys, os, pwd;
26 from userdir_ldap import *;
27 from userdir_gpg import *;
29 HavePrivateList = getattr(ConfModule, "haveprivatelist", True)
31 # This tries to search for a free UID. There are two possible ways to do
32 # this, one is to fetch all the entires and pick the highest, the other
33 # is to randomly guess uids until one is free. This uses the former.
34 # Regrettably ldap doesn't have an integer attribute comparision function
35 # so we can only cut the search down slightly
37 # [JT] This is broken with Woody LDAP and the Schema; for now just
38 # search through all UIDs.
40 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
41 "uidNumber=*",["uidNumber", "gidNumber"]);
45 ID = int(GetAttr(I,"uidNumber","0"));
46 gids.append(int(GetAttr(I, "gidNumber","0")))
50 resGID = HighestUID + 1;
54 return (HighestUID + 1, resGID);
57 AdminUser = pwd.getpwuid(os.getuid())[0];
62 OldGPGKeyRings = GPGKeyRings;
63 userdir_gpg.GPGKeyRings = [];
64 (options, arguments) = getopt.getopt(sys.argv[1:], "hu:man")
65 for (switch, val) in options:
67 print "Usage: ud-useradd <options>"
68 print "Available options:"
69 print " -h Show this help"
70 print " -u=<user> Admin user (defaults to current username)"
71 print " -m Force mail (for updates)"
72 print " -a Use old keyrings instead (??)"
73 print " -n Do not automatically assign UID/GIDs (useful for usergroups or non-default group membership"
75 elif (switch == '-u'):
77 elif (switch == '-m'):
79 elif (switch == '-a'):
80 userdir_gpg.GPGKeyRings = OldGPGKeyRings;
81 elif (switch == '-n'):
84 l = passwdAccessLDAP(BaseDn, AdminUser)
86 # Locate the key of the user we are adding
87 SetKeyrings(ConfModule.add_keyrings.split(":"))
89 Foo = raw_input("Who are you going to add (for a GPG search)? ");
93 Keys = GPGKeySearch(Foo);
96 print "Sorry, that search did not turn up any keys."
97 print "Has it been added to the Debian keyring already?"
100 print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
106 print "A matching key was found:"
107 GPGPrintKeyInfo(Keys[0]);
110 # Crack up the email address from the key into a best guess
111 # first/middle/last name
112 Addr = SplitEmail(Keys[0][2]);
113 (cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
114 emailaddr = Addr[1] + '@' + Addr[2];
121 # Decide if we should use IDEA encryption
123 while len(Keys[0][1]) < 40:
124 Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
132 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
134 print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
135 account = GetAttr(Attrs[0],"uid");
138 # Try to get a uniq account name
141 Res = raw_input("Login account [" + account + "]? ");
144 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
146 privsub = "%s@debian.org"%(account);
148 Res = raw_input("That account already exists, update [No/yes]? ");
150 # Update mode, fetch the default values from the directory
152 privsub = GetAttr(Attrs[0],"privateSub");
153 gidNumber = GetAttr(Attrs[0],"gidNumber");
154 uidNumber = GetAttr(Attrs[0],"uidNumber");
155 emailaddr = GetAttr(Attrs[0],"emailForward");
156 cn = GetAttr(Attrs[0],"cn");
157 sn = GetAttr(Attrs[0],"sn");
158 mn = GetAttr(Attrs[0],"mn");
159 if privsub == None or privsub == "":
165 # Prompt for the first/last name and email address
166 Res = raw_input("First name [" + cn + "]? ");
169 Res = raw_input("Middle name [" + mn + "]? ");
174 Res = raw_input("Last name [" + sn + "]? ");
177 Res = raw_input("Email forwarding address [" + emailaddr + "]? ");
181 # Debian-Private subscription
183 Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
189 (uidNumber, generatedGID) = GetFreeID(l)
191 gidNumber = DefaultGID
197 Res = raw_input("User ID Number [%s]? " % (uidNumber));
202 Res = raw_input("Group ID Number (default group is %s, new usergroup %s) [%s]" % (DefaultGID, generatedGID, gidNumber));
205 gidNumber = int(Res);
207 gidNumber = Group2GID(l, Res);
209 if gidNumber == generatedGID:
212 # Generate a random password
213 if Update == 0 or ForceMail == 1:
214 Password = raw_input("User's Password (Enter for random)? ");
217 print "Randomizing and encrypting password"
218 Password = GenPass();
219 Pass = HashPass(Password);
221 # Use GPG to encrypt it, pass the fingerprint to ID it
222 CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
223 "0x"+Keys[0][1],UsePGP2);
225 if CryptedPass == None:
226 raise "Error","Password Encryption failed"
228 Pass = HashPass(Password);
229 CryptedPass = "Your password has been set to the previously agreed value.";
234 # Now we have all the bits of information.
236 FullName = "%s %s %s" % (cn,mn,sn);
238 FullName = "%s %s" % (cn,sn);
239 print "------------";
240 print "Final information collected:"
241 print " %s <%s@%s>:" % (FullName,account,EmailAppend);
242 print " Assigned UID:",uidNumber," GID:", gidNumber;
243 print " Email forwarded to:",emailaddr
245 print " Private Subscription:",privsub;
246 print " GECOS Field: \"%s,,,,\"" % (FullName);
247 print " Login Shell: /bin/bash";
248 print " Key Fingerprint:",Keys[0][1];
249 Res = raw_input("Continue [No/yes]? ");
253 # Initialize the substitution Map
258 encrealname = FullName.decode('us-ascii')
260 encrealname = str(email.Header.Header(FullName, 'utf-8', 200))
262 Subst["__ENCODED_REALNAME__"] = encrealname
263 Subst["__REALNAME__"] = FullName;
264 Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0];
265 Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
266 Subst["__LOGIN__"] = account;
267 Subst["__PRIVATE__"] = privsub;
268 Subst["__EMAIL__"] = emailaddr
269 Subst["__PASSWORD__"] = CryptedPass;
271 # Submit the modification request
272 Dn = "uid=" + account + "," + BaseDn;
273 print "Updating LDAP directory..",
278 Details = [("uid",account),
279 ("objectClass", UserObjectClasses),
280 ("uidNumber",str(uidNumber)),
281 ("gidNumber",str(gidNumber)),
282 ("gecos",FullName+",,,,"),
283 ("loginShell","/bin/bash"),
284 ("keyFingerPrint",Keys[0][1]),
287 ("emailForward",emailaddr),
288 ("shadowLastChange",str(int(time.time()/24/60/60))),
290 ("shadowMax","99999"),
291 ("shadowWarning","7"),
292 ("userPassword","{crypt}"+Pass)];
294 Details.append(("mn",mn));
296 Details.append(("privateSub",privsub))
299 #Add user group if needed, then the actual user:
301 Dn = "gid=" + account + "," + BaseDn;
302 l.add_s(Dn,[("gid",account), ("gidNumber",str(gidNumber)), ("objectClass", GroupObjectClasses)])
305 Rec = [(ldap.MOD_REPLACE,"uidNumber",str(uidNumber)),
306 (ldap.MOD_REPLACE,"gidNumber",str(gidNumber)),
307 (ldap.MOD_REPLACE,"gecos",FullName+",,,,"),
308 (ldap.MOD_REPLACE,"loginShell","/bin/bash"),
309 (ldap.MOD_REPLACE,"keyFingerPrint",Keys[0][1]),
310 (ldap.MOD_REPLACE,"cn",cn),
311 (ldap.MOD_REPLACE,"mn",mn),
312 (ldap.MOD_REPLACE,"sn",sn),
313 (ldap.MOD_REPLACE,"emailForward",emailaddr),
314 (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60))),
315 (ldap.MOD_REPLACE,"shadowMin","0"),
316 (ldap.MOD_REPLACE,"shadowMax","99999"),
317 (ldap.MOD_REPLACE,"shadowWarning","7"),
318 (ldap.MOD_REPLACE,"shadowInactive",""),
319 (ldap.MOD_REPLACE,"shadowExpire","")];
321 Rec.append((ldap.MOD_REPLACE,"privateSub",privsub));
323 Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass));
329 # Abort email sends for an update operation
330 if Update == 1 and ForceMail == 0:
331 print "Account is not new, Not sending mails"
334 # Send the Welcome message
335 print "Sending Welcome Email"
336 templatepath = TemplatesDir + "/welcome-message-%d" % int(gidNumber)
337 if not os.path.exists(templatepath):
338 templatepath = TemplatesDir + "/welcome-message"
339 Reply = TemplateSubst(Subst,open(templatepath, "r").read())
340 Child = os.popen("/usr/sbin/sendmail -t","w");
341 #Child = os.popen("cat","w");
343 if Child.close() != None:
344 raise Error, "Sendmail gave a non-zero return code";
348 # vim:set shiftwidth=3: