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