]> git.decadent.org.uk Git - dak.git/blob - dak/add_user.py
convert add_user to SQLa
[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 queue
30 from daklib import utils
31 from daklib.dbconn import DBConn, add_database_user, get_or_set_uid
32 from daklib.regexes import re_gpg_fingerprint, re_user_address, re_user_mails, re_user_name
33
34 ################################################################################
35
36 Cnf = 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
112     keyrings = None
113
114     Cnf = 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     session = DBConn().session()
133
134     if not keyrings:
135         keyrings = Cnf.ValueList("Dinstall::GPGKeyring")
136
137 # Ignore the PGP keyring for download of new keys. Ignore errors, if key is missing it will
138 # barf with the next commands.
139     cmd = "gpg --no-secmem-warning --no-default-keyring %s --recv-keys %s" \
140            % (utils.gpg_keyring_args(keyrings), Cnf["Add-User::Options::Key"])
141     (result, output) = commands.getstatusoutput(cmd)
142
143     cmd = "gpg --with-colons --no-secmem-warning --no-auto-check-trustdb --no-default-keyring %s --with-fingerprint --list-key %s" \
144            % (utils.gpg_keyring_args(keyrings),
145               Cnf["Add-User::Options::Key"])
146     (result, output) = commands.getstatusoutput(cmd)
147     m = re_gpg_fingerprint.search(output)
148     if not m:
149         print output
150         utils.fubar("0x%s: (1) No fingerprint found in gpg output but it returned 0?\n%s" \
151                                         % (Cnf["Add-User::Options::Key"], utils.prefix_multi_line_string(output, \
152                                                                                                                                                                 " [GPG output:] ")))
153     primary_key = m.group(1)
154     primary_key = primary_key.replace(" ","")
155
156     uid = ""
157     if Cnf.has_key("Add-User::Options::User") and Cnf["Add-User::Options::User"]:
158         uid = Cnf["Add-User::Options::User"]
159         name = Cnf["Add-User::Options::User"]
160     else:
161         u = re_user_address.search(output)
162         if not u:
163             print output
164             utils.fubar("0x%s: (2) No userid found in gpg output but it returned 0?\n%s" \
165                         % (Cnf["Add-User::Options::Key"], utils.prefix_multi_line_string(output, " [GPG output:] ")))
166         uid = u.group(1)
167         n = re_user_name.search(output)
168         name = n.group(1)
169
170 # Look for all email addresses on the key.
171     emails=[]
172     for line in output.split('\n'):
173         e = re_user_mails.search(line)
174         if not e:
175             continue
176         emails.append(e.group(2))
177
178
179     print "0x%s -> %s <%s> -> %s -> %s" % (Cnf["Add-User::Options::Key"], name, emails[0], uid, primary_key)
180
181     prompt = "Add user %s with above data (y/N) ? " % (uid)
182     yn = utils.our_raw_input(prompt).lower()
183
184     if yn == "y":
185 # Create an account for the user?
186           summary = ""
187           if Cnf.FindB("Add-User::CreateAccount") or Cnf["Add-User::Options::Create"]:
188               password = GenPass()
189               pwcrypt = HashPass(password)
190               if Cnf.has_key("Add-User::GID"):
191                   cmd = "sudo /usr/sbin/useradd -g users -m -p '%s' -c '%s' -G %s %s" \
192                          % (pwcrypt, name, Cnf["Add-User::GID"], uid)
193               else:
194                   cmd = "sudo /usr/sbin/useradd -g users -m -p '%s' -c '%s' %s" \
195                          % (pwcrypt, name, uid)
196               (result, output) = commands.getstatusoutput(cmd)
197               if (result != 0):
198                    utils.fubar("Invocation of '%s' failed:\n%s\n" % (cmd, output), result)
199               try:
200                   summary+=createMail(uid, password, Cnf["Add-User::Options::Key"], Cnf["Dinstall::GPGKeyring"])
201               except:
202                   summary=""
203                   utils.warn("Could not prepare password information for mail, not sending password.")
204
205 # Now add user to the database.
206           # Note that we provide a session, so we're responsible for committing
207           uidobj = get_or_set_uid(uid, session=session)
208           uid_id = uidobj.uid_id
209           add_database_user(uid)
210           session.commit()
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 = database.get_or_set_maintainer_id(name)
216 #          session.execute("INSERT INTO fingerprint (fingerprint, uid) VALUES (:fingerprint, uid)",
217 #                          {'fingerprint': primary_key, 'uid': uid_id})
218
219 # Lets add user to the email-whitelist file if its configured.
220           if Cnf.has_key("Dinstall::MailWhiteList") and Cnf["Dinstall::MailWhiteList"] != "":
221               file = utils.open_file(Cnf["Dinstall::MailWhiteList"], "a")
222               for mail in emails:
223                   file.write(mail+'\n')
224               file.close()
225
226           print "Added:\nUid:\t %s (ID: %s)\nMaint:\t %s\nFP:\t %s" % (uid, uid_id, \
227                      name, primary_key)
228
229 # Should we send mail to the newly added user?
230           if Cnf.FindB("Add-User::SendEmail"):
231               mail = name + "<" + emails[0] +">"
232               Upload = queue.Upload(Cnf)
233               Subst = Upload.Subst
234               Subst["__NEW_MAINTAINER__"] = mail
235               Subst["__UID__"] = uid
236               Subst["__KEYID__"] = Cnf["Add-User::Options::Key"]
237               Subst["__PRIMARY_KEY__"] = primary_key
238               Subst["__FROM_ADDRESS__"] = Cnf["Dinstall::MyEmailAddress"]
239               Subst["__HOSTNAME__"] = Cnf["Dinstall::MyHost"]
240               Subst["__SUMMARY__"] = summary
241               new_add_message = utils.TemplateSubst(Subst,Cnf["Dir::Templates"]+"/add-user.added")
242               utils.send_mail(new_add_message)
243
244     else:
245           uid = None
246
247
248 #######################################################################################
249
250 if __name__ == '__main__':
251     main()
252