]> git.decadent.org.uk Git - dak.git/blob - dak/add_user.py
tidy up add_user
[dak.git] / dak / add_user.py
1 #!/usr/bin/env python
2
3 """
4 Add a user to to the uid/maintainer/fingerprint table and
5 add his key to the GPGKeyring
6
7 @contact: Debian FTP Master <ftpmaster@debian.org>
8 @copyright: 2004, 2009  Joerg Jaspert <joerg@ganneff.de>
9 @license: GNU General Public License version 2 or later
10 """
11
12 ################################################################################
13 # <elmo> wow, sounds like it'll be a big step up.. configuring dak on a
14 #        new machine even scares me :)
15 ################################################################################
16
17 # You don't want to read this script if you know python.
18 # I know what I say. I dont know python and I wrote it. So go and read some other stuff.
19
20 import commands
21 import pg
22 import re
23 import sys
24 import time
25 import os
26 import apt_pkg
27
28 from daklib import daklog
29 from daklib import utils
30 from daklib.dbconn import DBConn, add_database_user, get_or_set_uid
31 from daklib.regexes import re_gpg_fingerprint, re_user_address, re_user_mails, re_user_name
32
33 ################################################################################
34
35 Cnf = None
36 Logger = None
37
38 ################################################################################
39
40 def usage(exit_code=0):
41     print """Usage: add-user [OPTION]...
42 Adds a new user to the dak databases and keyrings
43
44     -k, --key                keyid of the User
45     -u, --user               userid of the User
46     -c, --create             create a system account for the user
47     -h, --help               show this help and exit."""
48     sys.exit(exit_code)
49
50 ################################################################################
51 # Stolen from userdir-ldap
52 # Compute a random password using /dev/urandom.
53 def GenPass():
54    # Generate a 10 character random string
55    SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/."
56    Rand = open("/dev/urandom")
57    Password = ""
58    for i in range(0,15):
59       Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]
60    return Password
61
62 # Compute the MD5 crypted version of the given password
63 def HashPass(Password):
64    import crypt
65    # Hash it telling glibc to use the MD5 algorithm - if you dont have
66    # glibc then just change Salt = "$1$" to Salt = ""
67    SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/."
68    Salt  = "$1$"
69    Rand = open("/dev/urandom")
70    for x in range(0,10):
71       Salt = Salt + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)]
72    Pass = crypt.crypt(Password,Salt)
73    if len(Pass) < 14:
74       raise "Password Error", "MD5 password hashing failed, not changing the password!"
75    return Pass
76
77 ################################################################################
78
79 def createMail(login, passwd, keyid, keyring):
80     import GnuPGInterface
81
82     message= """
83
84 Additionally there is now an account created for you.
85
86 """
87     message+= "\nYour password for the login %s is: %s\n" % (login, passwd)
88
89     gnupg = GnuPGInterface.GnuPG()
90     gnupg.options.armor = 1
91     gnupg.options.meta_interactive = 0
92     gnupg.options.extra_args.append("--no-default-keyring")
93     gnupg.options.extra_args.append("--always-trust")
94     gnupg.options.extra_args.append("--no-secmem-warning")
95     gnupg.options.extra_args.append("--keyring=%s" % keyring)
96     gnupg.options.recipients = [keyid]
97     proc = gnupg.run(['--encrypt'], create_fhs=['stdin', 'stdout'])
98     proc.handles['stdin'].write(message)
99     proc.handles['stdin'].close()
100     output = proc.handles['stdout'].read()
101     proc.handles['stdout'].close()
102     proc.wait()
103     return output
104
105 ################################################################################
106
107 def main():
108     global Cnf
109     keyrings = None
110
111     Cnf = utils.get_conf()
112
113     Arguments = [('h',"help","Add-User::Options::Help"),
114                  ('c',"create","Add-User::Options::Create"),
115                  ('k',"key","Add-User::Options::Key", "HasArg"),
116                  ('u',"user","Add-User::Options::User", "HasArg"),
117                  ]
118
119     for i in [ "help", "create" ]:
120         if not Cnf.has_key("Add-User::Options::%s" % (i)):
121             Cnf["Add-User::Options::%s" % (i)] = ""
122
123     apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv)
124
125     Options = Cnf.SubTree("Add-User::Options")
126     if Options["help"]:
127         usage()
128
129     session = DBConn().session()
130
131     if not keyrings:
132         keyrings = Cnf.ValueList("Dinstall::GPGKeyring")
133
134 # Ignore the PGP keyring for download of new keys. Ignore errors, if key is missing it will
135 # barf with the next commands.
136     cmd = "gpg --no-secmem-warning --no-default-keyring %s --recv-keys %s" \
137            % (utils.gpg_keyring_args(keyrings), Cnf["Add-User::Options::Key"])
138     (result, output) = commands.getstatusoutput(cmd)
139
140     cmd = "gpg --with-colons --no-secmem-warning --no-auto-check-trustdb --no-default-keyring %s --with-fingerprint --list-key %s" \
141            % (utils.gpg_keyring_args(keyrings),
142               Cnf["Add-User::Options::Key"])
143     (result, output) = commands.getstatusoutput(cmd)
144     m = re_gpg_fingerprint.search(output)
145     if not m:
146         print output
147         utils.fubar("0x%s: (1) No fingerprint found in gpg output but it returned 0?\n%s" \
148                                         % (Cnf["Add-User::Options::Key"], utils.prefix_multi_line_string(output, \
149                                                                                                                                                                 " [GPG output:] ")))
150     primary_key = m.group(1)
151     primary_key = primary_key.replace(" ","")
152
153     uid = ""
154     if Cnf.has_key("Add-User::Options::User") and Cnf["Add-User::Options::User"]:
155         uid = Cnf["Add-User::Options::User"]
156         name = Cnf["Add-User::Options::User"]
157     else:
158         u = re_user_address.search(output)
159         if not u:
160             print output
161             utils.fubar("0x%s: (2) No userid found in gpg output but it returned 0?\n%s" \
162                         % (Cnf["Add-User::Options::Key"], utils.prefix_multi_line_string(output, " [GPG output:] ")))
163         uid = u.group(1)
164         n = re_user_name.search(output)
165         name = n.group(1)
166
167 # Look for all email addresses on the key.
168     emails=[]
169     for line in output.split('\n'):
170         e = re_user_mails.search(line)
171         if not e:
172             continue
173         emails.append(e.group(2))
174
175
176     print "0x%s -> %s <%s> -> %s -> %s" % (Cnf["Add-User::Options::Key"], name, emails[0], uid, primary_key)
177
178     prompt = "Add user %s with above data (y/N) ? " % (uid)
179     yn = utils.our_raw_input(prompt).lower()
180
181     if yn == "y":
182 # Create an account for the user?
183           summary = ""
184           if Cnf.FindB("Add-User::CreateAccount") or Cnf["Add-User::Options::Create"]:
185               password = GenPass()
186               pwcrypt = HashPass(password)
187               if Cnf.has_key("Add-User::GID"):
188                   cmd = "sudo /usr/sbin/useradd -g users -m -p '%s' -c '%s' -G %s %s" \
189                          % (pwcrypt, name, Cnf["Add-User::GID"], uid)
190               else:
191                   cmd = "sudo /usr/sbin/useradd -g users -m -p '%s' -c '%s' %s" \
192                          % (pwcrypt, name, uid)
193               (result, output) = commands.getstatusoutput(cmd)
194               if (result != 0):
195                    utils.fubar("Invocation of '%s' failed:\n%s\n" % (cmd, output), result)
196               try:
197                   summary+=createMail(uid, password, Cnf["Add-User::Options::Key"], Cnf["Dinstall::GPGKeyring"])
198               except:
199                   summary=""
200                   utils.warn("Could not prepare password information for mail, not sending password.")
201
202 # Now add user to the database.
203           # Note that we provide a session, so we're responsible for committing
204           uidobj = get_or_set_uid(uid, session=session)
205           uid_id = uidobj.uid_id
206           add_database_user(uid)
207           session.commit()
208 # The following two are kicked out in rhona, so we don't set them. kelly adds
209 # them as soon as she installs a package with unknown ones, so no problems to expect here.
210 # Just leave the comment in, to not think about "Why the hell aren't they added" in
211 # a year, if we ever touch uma again.
212 #          maint_id = database.get_or_set_maintainer_id(name)
213 #          session.execute("INSERT INTO fingerprint (fingerprint, uid) VALUES (:fingerprint, uid)",
214 #                          {'fingerprint': primary_key, 'uid': uid_id})
215
216 # Lets add user to the email-whitelist file if its configured.
217           if Cnf.has_key("Dinstall::MailWhiteList") and Cnf["Dinstall::MailWhiteList"] != "":
218               file = utils.open_file(Cnf["Dinstall::MailWhiteList"], "a")
219               for mail in emails:
220                   file.write(mail+'\n')
221               file.close()
222
223           print "Added:\nUid:\t %s (ID: %s)\nMaint:\t %s\nFP:\t %s" % (uid, uid_id, \
224                      name, primary_key)
225
226 # Should we send mail to the newly added user?
227           if Cnf.FindB("Add-User::SendEmail"):
228               mail = name + "<" + emails[0] +">"
229               Subst = {}
230               Subst["__NEW_MAINTAINER__"] = mail
231               Subst["__UID__"] = uid
232               Subst["__KEYID__"] = Cnf["Add-User::Options::Key"]
233               Subst["__PRIMARY_KEY__"] = primary_key
234               Subst["__FROM_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
235               Subst["__HOSTNAME__"] = Cnf["Dinstall::MyHost"]
236               Subst["__SUMMARY__"] = summary
237               new_add_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/add-user.added")
238               utils.send_mail(new_add_message)
239
240     else:
241           uid = None
242
243
244 #######################################################################################
245
246 if __name__ == '__main__':
247     main()
248