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