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 

23import sys 

24 

25import apt_pkg 

26from sqlalchemy.orm.exc import NoResultFound 

27 

28from daklib.config import Config 

29from daklib.dbconn import DBConn, Fingerprint, Keyring, Uid, get_keyring 

30 

31# Globals 

32Options = None 

33 

34################################################################################ 

35 

36 

37def 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 

48def 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 

58def 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 

77def 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 

92def 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: 113 ↛ 104line 113 didn't jump to line 104

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"]: 128 ↛ 129line 128 didn't jump to line 129, because the condition on line 128 was never true

129 list_uids(session, Options["List-UIDs"]) 

130 sys.exit(0) 

131 

132 if len(keyring_names) != 1: 132 ↛ 133line 132 didn't jump to line 133, because the condition on line 132 was never true

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: 145 ↛ 146line 145 didn't jump to line 146, because the condition on line 145 was never true

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"]: 152 ↛ 156line 152 didn't jump to line 156, because the condition on line 152 was never false

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: 169 ↛ 165line 169 didn't jump to line 165, because the condition on line 169 was never false

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: 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true

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: 195 ↛ 198line 195 didn't jump to line 198, because the condition on line 195 was never false

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: 218 ↛ 219line 218 didn't jump to line 219, because the condition on line 218 was never true

219 olduid = -1 

220 

221 if oldkid is None: 221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true

222 oldkid = -1 

223 

224 if oldfid == -1: 224 ↛ 236line 224 didn't jump to line 236, because the condition on line 224 was never false

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: 229 ↛ 232line 229 didn't jump to line 232, because the condition on line 229 was never false

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"]: 279 ↛ 280line 279 didn't jump to line 280, because the condition on line 279 was never true

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 

298if __name__ == "__main__": 

299 main()