Package dak :: Module import_keyring
[hide private]
[frames] | no frames]

Source Code for Module dak.import_keyring

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