]> git.decadent.org.uk Git - dak.git/blob - dak/import_keyring.py
order files so that Packages/Sources files make sense and can be rsync'd sanely
[dak.git] / dak / import_keyring.py
1 #!/usr/bin/env python
2
3 """ Imports a keyring into the database """
4 # Copyright (C) 2007  Anthony Towns <aj@erisian.com.au>
5 # Copyright (C) 2009  Mark Hymers <mhy@debian.org>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 ################################################################################
22
23 import sys, os, re
24 import apt_pkg, ldap
25
26 from daklib.config import Config
27 from daklib.dbconn import *
28
29 # Globals
30 Options = None
31
32 ################################################################################
33
34 def get_uid_info(session):
35     byname = {}
36     byid = {}
37     q = session.execute("SELECT id, uid, name FROM uid")
38     for (keyid, uid, name) in q.fetchall():
39         byname[uid] = (keyid, name)
40         byid[keyid] = (uid, name)
41
42     return (byname, byid)
43
44 def get_fingerprint_info(session):
45     fins = {}
46     q = session.execute("SELECT f.fingerprint, f.id, f.uid, f.keyring FROM fingerprint f")
47     for (fingerprint, fingerprint_id, uid, keyring) in q.fetchall():
48         fins[fingerprint] = (uid, fingerprint_id, keyring)
49     return fins
50
51 ################################################################################
52
53 def usage (exit_code=0):
54     print """Usage: dak import-keyring [OPTION]... [KEYRING]
55   -h, --help                  show this help and exit.
56   -L, --import-ldap-users     generate uid entries for keyring from LDAP
57   -U, --generate-users FMT    generate uid entries from keyring as FMT"""
58     sys.exit(exit_code)
59
60
61 ################################################################################
62
63 def main():
64     global Options
65
66     cnf = Config()
67     Arguments = [('h',"help","Import-Keyring::Options::Help"),
68                  ('L',"import-ldap-users","Import-Keyring::Options::Import-Ldap-Users"),
69                  ('U',"generate-users","Import-Keyring::Options::Generate-Users", "HasArg"),
70                 ]
71
72     for i in [ "help", "report-changes", "generate-users", "import-ldap-users" ]:
73         if not cnf.has_key("Import-Keyring::Options::%s" % (i)):
74             cnf["Import-Keyring::Options::%s" % (i)] = ""
75
76     keyring_names = apt_pkg.ParseCommandLine(cnf.Cnf, Arguments, sys.argv)
77
78     ### Parse options
79
80     Options = cnf.SubTree("Import-Keyring::Options")
81
82     if Options["Help"]:
83         usage()
84
85     if len(keyring_names) != 1:
86         usage(1)
87
88     ### Keep track of changes made
89     changes = []   # (uid, changes strings)
90
91     ### Initialise
92     session = DBConn().session()
93
94     ### Cache all the existing fingerprint entries
95     db_fin_info = get_fingerprint_info(session)
96
97     ### Parse the keyring
98
99     keyringname = keyring_names[0]
100     keyring = get_keyring(keyringname, session)
101     if not keyring:
102         print "E: Can't load keyring %s from database" % keyringname
103         sys.exit(1)
104
105     keyring.load_keys(keyringname)
106
107     ### Generate new uid entries if they're needed (from LDAP or the keyring)
108     if Options["Generate-Users"]:
109         format = Options["Generate-Users"]
110         (desuid_byname, desuid_byid) = keyring.generate_users_from_keyring(Options["Generate-Users"], session)
111     elif Options["Import-Ldap-Users"]:
112         (desuid_byname, desuid_byid) = keyring.import_users_from_ldap(session)
113     else:
114         (desuid_byname, desuid_byid) = ({}, {})
115
116     ### Cache all the existing uid entries
117     (db_uid_byname, db_uid_byid) = get_uid_info(session)
118
119     ### Update full names of applicable users
120     for keyid in desuid_byid.keys():
121         uid = (keyid, desuid_byid[keyid][0])
122         name = desuid_byid[keyid][1]
123         oname = db_uid_byid[keyid][1]
124         if name and oname != name:
125             changes.append((uid[1], "Full name: %s" % (name)))
126             session.execute("UPDATE uid SET name = :name WHERE id = :keyid",
127                             {'name': name, 'keyid': keyid})
128
129     # The fingerprint table (fpr) points to a uid and a keyring.
130     #   If the uid is being decided here (ldap/generate) we set it to it.
131     #   Otherwise, if the fingerprint table already has a uid (which we've
132     #     cached earlier), we preserve it.
133     #   Otherwise we leave it as None
134
135     fpr = {}
136     for z in keyring.keys.keys():
137         keyid = db_uid_byname.get(keyring.keys[z].get("uid", None), [None])[0]
138         if keyid == None:
139             keyid = db_fin_info.get(keyring.keys[z]["fingerprints"][0], [None])[0]
140         for y in keyring.keys[z]["fingerprints"]:
141             fpr[y] = (keyid, keyring.keyring_id)
142
143     # For any keys that used to be in this keyring, disassociate them.
144     # We don't change the uid, leaving that for historical info; if
145     # the id should change, it'll be set when importing another keyring.
146
147     for f,(u,fid,kr) in db_fin_info.iteritems():
148         if kr != keyring.keyring_id:
149             continue
150
151         if f in fpr:
152             continue
153
154         changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f)))
155         session.execute("""UPDATE fingerprint
156                               SET keyring = NULL,
157                                   source_acl_id = NULL,
158                                   binary_acl_id = NULL,
159                                   binary_reject = TRUE
160                             WHERE id = :fprid""", {'fprid': fid})
161
162         session.execute("""DELETE FROM binary_acl_map WHERE fingerprint_id = :fprid""", {'fprid': fid})
163
164     # For the keys in this keyring, add/update any fingerprints that've
165     # changed.
166
167     for f in fpr:
168         newuid = fpr[f][0]
169         newuiduid = db_uid_byid.get(newuid, [None])[0]
170
171         (olduid, oldfid, oldkid) = db_fin_info.get(f, [-1,-1,-1])
172
173         if olduid == None:
174             olduid = -1
175
176         if oldkid == None:
177             oldkid = -1
178
179         if oldfid == -1:
180             changes.append((newuiduid, "Added key: %s" % (f)))
181             fp = Fingerprint()
182             fp.fingerprint = f
183             fp.keyring_id = keyring.keyring_id
184             if newuid:
185                 fp.uid_id = newuid
186
187             fp.binary_acl_id = keyring.default_binary_acl_id
188             fp.source_acl_id = keyring.default_source_acl_id
189             fp.default_binary_reject = keyring.default_binary_reject
190             session.add(fp)
191             session.flush()
192
193             for k in keyring.keyring_acl_map:
194                 ba = BinaryACLMap()
195                 ba.fingerprint_id = fp.fingerprint_id
196                 ba.architecture_id = k.architecture_id
197                 session.add(ba)
198                 session.flush()
199
200         else:
201             if newuid and olduid != newuid:
202                 if olduid != -1:
203                     changes.append((newuiduid, "Linked key: %s" % f))
204                     changes.append((newuiduid, "  (formerly belonging to %s)" % (db_uid_byid[olduid][0])))
205                 else:
206                     changes.append((newuiduid, "Linked key: %s" % f))
207                     changes.append((newuiduid, "  (formerly unowned)"))
208
209                 session.execute("UPDATE fingerprint SET uid = :uid WHERE id = :fpr",
210                                 {'uid': newuid, 'fpr': oldfid})
211
212             # Don't move a key from a keyring with a higher priority to a lower one
213             if oldkid != keyring.keyring_id:
214                 movekey = False
215                 if oldkid == -1:
216                     movekey = True
217                 else:
218                     try:
219                         oldkeyring = session.query(Keyring).filter_by(keyring_id=oldkid).one()
220                     except NotFoundError:
221                         print "ERROR: Cannot find old keyring with id %s" % oldkid
222                         sys.exit(1)
223
224                     if oldkeyring.priority < keyring.priority:
225                         movekey = True
226
227                 # Only change the keyring if it won't result in a loss of permissions
228                 if movekey:
229                     session.execute("""DELETE FROM binary_acl_map WHERE fingerprint_id = :fprid""", {'fprid': oldfid})
230
231                     session.execute("""UPDATE fingerprint
232                                           SET keyring = :keyring,
233                                               source_acl_id = :source_acl_id,
234                                               binary_acl_id = :binary_acl_id,
235                                               binary_reject = :binary_reject
236                                         WHERE id = :fpr""",
237                                     {'keyring': keyring.keyring_id,
238                                      'source_acl_id': keyring.default_source_acl_id,
239                                      'binary_acl_id': keyring.default_binary_acl_id,
240                                      'binary_reject': keyring.default_binary_reject,
241                                      'fpr': oldfid})
242
243                     session.flush()
244
245                     for k in keyring.keyring_acl_map:
246                         ba = BinaryACLMap()
247                         ba.fingerprint_id = oldfid
248                         ba.architecture_id = k.architecture_id
249                         session.add(ba)
250                         session.flush()
251
252                 else:
253                     print "Key %s exists in both %s and %s keyrings. Not demoting." % (oldkeyring.keyring_name,
254                                                                                        keyring.keyring_name)
255
256     # All done!
257     session.commit()
258
259     # Print a summary
260     changesd = {}
261     for (k, v) in changes:
262         if k not in changesd:
263             changesd[k] = ""
264         changesd[k] += "    %s\n" % (v)
265
266     keys = changesd.keys()
267     keys.sort()
268     for k in keys:
269         print "%s\n%s\n" % (k, changesd[k])
270
271 ################################################################################
272
273 if __name__ == '__main__':
274     main()