1#! /usr/bin/env python3
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>
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.
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.
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
21################################################################################
23import sys
24import apt_pkg
26from daklib.config import Config
27from daklib.dbconn import *
29# Globals
30Options = None
32################################################################################
35def get_uid_info(session):
36 byname = {}
37 byid = {}
38 q = session.execute("SELECT id, uid, name FROM uid")
39 for (keyid, uid, name) in q.fetchall():
40 byname[uid] = (keyid, name)
41 byid[keyid] = (uid, name)
43 return (byname, byid)
46def get_fingerprint_info(session):
47 fins = {}
48 q = session.execute("SELECT f.fingerprint, f.id, f.uid, f.keyring FROM fingerprint f")
49 for (fingerprint, fingerprint_id, uid, keyring) in q.fetchall():
50 fins[fingerprint] = (uid, fingerprint_id, keyring)
51 return fins
54def list_uids(session, pattern):
55 sql_pattern = f"%{pattern}%"
56 message = "List UIDs matching pattern %s" % sql_pattern
57 message += "\n" + ("=" * len(message))
58 print(message)
59 uid_query = session.query(Uid).filter(Uid.uid.ilike(sql_pattern))
60 for uid in uid_query.all():
61 print("\nuid %s" % uid.uid)
62 for fp in uid.fingerprint:
63 print(" fingerprint %s" % fp.fingerprint)
64 keyring = "unknown"
65 if fp.keyring:
66 keyring = fp.keyring.keyring_name
67 print(" keyring %s" % keyring)
69################################################################################
72def usage(exit_code=0):
73 print("""Usage: dak import-keyring [OPTION]... [KEYRING]
74 -h, --help show this help and exit.
75 -L, --import-ldap-users generate uid entries for keyring from LDAP
76 -U, --generate-users FMT generate uid entries from keyring as FMT
77 -l, --list-uids STRING list all uids matching *STRING*
78 -n, --no-action don't change database""")
79 sys.exit(exit_code)
82################################################################################
84def main():
85 global Options
87 cnf = Config()
88 Arguments = [('h', "help", "Import-Keyring::Options::Help"),
89 ('L', "import-ldap-users", "Import-Keyring::Options::Import-Ldap-Users"),
90 ('U', "generate-users", "Import-Keyring::Options::Generate-Users", "HasArg"),
91 ('l', "list-uids", "Import-Keyring::Options::List-UIDs", "HasArg"),
92 ('n', "no-action", "Import-Keyring::Options::No-Action"),
93 ]
95 for i in ["help", "report-changes", "generate-users",
96 "import-ldap-users", "list-uids", "no-action"]:
97 key = "Import-Keyring::Options::%s" % i
98 if key not in cnf: 98 ↛ 95line 98 didn't jump to line 95, because the condition on line 98 was never false
99 cnf[key] = ""
101 keyring_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
103 ### Parse options
105 Options = cnf.subtree("Import-Keyring::Options")
107 if Options["Help"]:
108 usage()
110 ### Initialise
111 session = DBConn().session()
113 if Options["List-UIDs"]: 113 ↛ 114line 113 didn't jump to line 114, because the condition on line 113 was never true
114 list_uids(session, Options["List-UIDs"])
115 sys.exit(0)
117 if len(keyring_names) != 1: 117 ↛ 118line 117 didn't jump to line 118, because the condition on line 117 was never true
118 usage(1)
120 ### Keep track of changes made
121 changes = [] # (uid, changes strings)
123 ### Cache all the existing fingerprint entries
124 db_fin_info = get_fingerprint_info(session)
126 ### Parse the keyring
128 keyringname = keyring_names[0]
129 keyring = get_keyring(keyringname, session)
130 if not keyring: 130 ↛ 131line 130 didn't jump to line 131, because the condition on line 130 was never true
131 print("E: Can't load keyring %s from database" % keyringname)
132 sys.exit(1)
134 keyring.load_keys(keyringname)
136 ### Generate new uid entries if they're needed (from LDAP or the keyring)
137 if Options["Generate-Users"]: 137 ↛ 140line 137 didn't jump to line 140, because the condition on line 137 was never false
138 format = Options["Generate-Users"]
139 (desuid_byname, desuid_byid) = keyring.generate_users_from_keyring(Options["Generate-Users"], session)
140 elif Options["Import-Ldap-Users"]:
141 (desuid_byname, desuid_byid) = keyring.import_users_from_ldap(session)
142 else:
143 (desuid_byname, desuid_byid) = ({}, {})
145 ### Cache all the existing uid entries
146 (db_uid_byname, db_uid_byid) = get_uid_info(session)
148 ### Update full names of applicable users
149 for keyid in desuid_byid.keys():
150 uid = (keyid, desuid_byid[keyid][0])
151 name = desuid_byid[keyid][1]
152 oname = db_uid_byid[keyid][1]
153 if name and oname != name: 153 ↛ 149line 153 didn't jump to line 149, because the condition on line 153 was never false
154 changes.append((uid[1], "Full name: %s" % (name)))
155 session.execute("UPDATE uid SET name = :name WHERE id = :keyid",
156 {'name': name, 'keyid': keyid})
158 # The fingerprint table (fpr) points to a uid and a keyring.
159 # If the uid is being decided here (ldap/generate) we set it to it.
160 # Otherwise, if the fingerprint table already has a uid (which we've
161 # cached earlier), we preserve it.
162 # Otherwise we leave it as None
164 fpr = {}
165 for z in keyring.keys.keys():
166 keyid = db_uid_byname.get(keyring.keys[z].get("uid", None), [None])[0]
167 if keyid is None: 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true
168 keyid = db_fin_info.get(keyring.keys[z]["fingerprints"][0], [None])[0]
169 for y in keyring.keys[z]["fingerprints"]:
170 fpr[y] = (keyid, keyring.keyring_id)
172 # For any keys that used to be in this keyring, disassociate them.
173 # We don't change the uid, leaving that for historical info; if
174 # the id should change, it'll be set when importing another keyring.
176 for f, (u, fid, kr) in db_fin_info.items():
177 if kr != keyring.keyring_id: 177 ↛ 180line 177 didn't jump to line 180, because the condition on line 177 was never false
178 continue
180 if f in fpr:
181 continue
183 changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f)))
184 session.execute("""UPDATE fingerprint
185 SET keyring = NULL
186 WHERE id = :fprid""", {'fprid': fid})
188 # For the keys in this keyring, add/update any fingerprints that've
189 # changed.
191 for f in fpr:
192 newuid = fpr[f][0]
193 newuiduid = db_uid_byid.get(newuid, [None])[0]
195 (olduid, oldfid, oldkid) = db_fin_info.get(f, [-1, -1, -1])
197 if olduid is None: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true
198 olduid = -1
200 if oldkid is None: 200 ↛ 201line 200 didn't jump to line 201, because the condition on line 200 was never true
201 oldkid = -1
203 if oldfid == -1: 203 ↛ 215line 203 didn't jump to line 215, because the condition on line 203 was never false
204 changes.append((newuiduid, "Added key: %s" % (f)))
205 fp = Fingerprint()
206 fp.fingerprint = f
207 fp.keyring_id = keyring.keyring_id
208 if newuid: 208 ↛ 211line 208 didn't jump to line 211, because the condition on line 208 was never false
209 fp.uid_id = newuid
211 session.add(fp)
212 session.flush()
214 else:
215 if newuid and olduid != newuid and olduid == -1:
216 changes.append((newuiduid, "Linked key: %s" % f))
217 changes.append((newuiduid, " (formerly unowned)"))
218 session.execute("UPDATE fingerprint SET uid = :uid WHERE id = :fpr",
219 {'uid': newuid, 'fpr': oldfid})
221 # Don't move a key from a keyring with a higher priority to a lower one
222 if oldkid != keyring.keyring_id:
223 movekey = False
224 if oldkid == -1:
225 movekey = True
226 else:
227 try:
228 oldkeyring = session.query(Keyring).filter_by(keyring_id=oldkid).one()
229 except NotFoundError:
230 print("ERROR: Cannot find old keyring with id %s" % oldkid)
231 sys.exit(1)
233 if oldkeyring.priority < keyring.priority:
234 movekey = True
236 # Only change the keyring if it won't result in a loss of permissions
237 if movekey:
238 session.execute("""UPDATE fingerprint
239 SET keyring = :keyring
240 WHERE id = :fpr""",
241 {'keyring': keyring.keyring_id,
242 'fpr': oldfid})
244 session.flush()
246 else:
247 print("Key %s exists in both %s and %s keyrings. Not demoting." % (f,
248 oldkeyring.keyring_name,
249 keyring.keyring_name))
251 # All done!
252 if Options["No-Action"]: 252 ↛ 253line 252 didn't jump to line 253, because the condition on line 252 was never true
253 session.rollback()
254 else:
255 session.commit()
257 # Print a summary
258 changesd = {}
259 for (k, v) in changes:
260 if k not in changesd:
261 changesd[k] = ""
262 changesd[k] += " %s\n" % (v)
264 for k in sorted(changesd):
265 print("%s\n%s\n" % (k, changesd[k]))
267################################################################################
270if __name__ == '__main__':
271 main()