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