Helpviewer/SslCertificatesDialog.py

changeset 762
48190a225699
parent 753
e19a516f0a97
child 771
8ab912f36143
equal deleted inserted replaced
761:de0723fab049 762:48190a225699
5 5
6 """ 6 """
7 Module implementing a dialog to show and edit all certificates. 7 Module implementing a dialog to show and edit all certificates.
8 """ 8 """
9 9
10 from PyQt4.QtCore import pyqtSlot, Qt 10 from PyQt4.QtCore import pyqtSlot, Qt, QByteArray, QFile, QFileInfo, QIODevice
11 from PyQt4.QtGui import QDialog, QTreeWidgetItem 11 from PyQt4.QtGui import QDialog, QTreeWidgetItem, QFileDialog
12 try: 12 try:
13 from PyQt4.QtNetwork import QSslCertificate, QSslSocket, QSslConfiguration 13 from PyQt4.QtNetwork import QSslCertificate, QSslSocket, QSslConfiguration, QSsl
14 except ImportError: 14 except ImportError:
15 pass 15 pass
16 16
17 from E5Gui import E5MessageBox 17 from E5Gui import E5MessageBox
18 18
94 @param previous previous current item (QTreeWidgetItem) 94 @param previous previous current item (QTreeWidgetItem)
95 """ 95 """
96 enable = current is not None and current.parent() is not None 96 enable = current is not None and current.parent() is not None
97 self.serversViewButton.setEnabled(enable) 97 self.serversViewButton.setEnabled(enable)
98 self.serversDeleteButton.setEnabled(enable) 98 self.serversDeleteButton.setEnabled(enable)
99 self.serversExportButton.setEnabled(enable)
99 100
100 @pyqtSlot() 101 @pyqtSlot()
101 def on_serversViewButton_clicked(self): 102 def on_serversViewButton_clicked(self):
102 """ 103 """
103 Private slot to show data of the selected server certificate. 104 Private slot to show data of the selected server certificate.
120 """ checks will be reinstantiated and the server has to""" 121 """ checks will be reinstantiated and the server has to"""
121 """ present a valid certificate.</p>""")\ 122 """ present a valid certificate.</p>""")\
122 .format(itm.text(0))) 123 .format(itm.text(0)))
123 if res: 124 if res:
124 server = itm.text(1) 125 server = itm.text(1)
126 cert = self.serversCertificatesTree.currentItem().data(0, self.CertRole)
125 127
126 # delete the selected entry and it's parent entry, if it was the only one 128 # delete the selected entry and it's parent entry, if it was the only one
127 parent = itm.parent() 129 parent = itm.parent()
128 parent.takeChild(parent.indexOfChild(itm)) 130 parent.takeChild(parent.indexOfChild(itm))
129 if parent.childCount() == 0: 131 if parent.childCount() == 0:
131 self.serversCertificatesTree.indexOfTopLevelItem(parent)) 133 self.serversCertificatesTree.indexOfTopLevelItem(parent))
132 134
133 # delete the certificate from the user certificate store 135 # delete the certificate from the user certificate store
134 certificateDict = Preferences.toDict( 136 certificateDict = Preferences.toDict(
135 Preferences.Prefs.settings.value("Help/CaCertificatesDict")) 137 Preferences.Prefs.settings.value("Help/CaCertificatesDict"))
136 del certificateDict[server] 138 if server in certificateDict:
139 certs = QSslCertificate.fromData(certificateDict[server])
140 if cert in certs:
141 certs.remove(cert)
142 if certs:
143 pems = QByteArray()
144 for cert in certs:
145 pems.append(cert.toPem() + '\n')
146 certificateDict[server] = pems
147 else:
148 del certificateDict[server]
137 Preferences.Prefs.settings.setValue("Help/CaCertificatesDict", 149 Preferences.Prefs.settings.setValue("Help/CaCertificatesDict",
138 certificateDict) 150 certificateDict)
139 151
140 # delete the certificate from the default certificates 152 # delete the certificate from the default certificates
141 caNew = [] 153 self.__updateDefaultConfiguration()
142 for topLevelIndex in range(self.serversCertificatesTree.topLevelItemCount()): 154
143 parent = self.serversCertificatesTree.topLevelItem(topLevelIndex) 155 @pyqtSlot()
144 for childIndex in range(parent.childCount()): 156 def on_serversImportButton_clicked(self):
145 cert = parent.child(childIndex).data(0, self.CertRole) 157 """
146 if cert not in caNew: 158 Private slot to import server certificates.
147 caNew.append(cert) 159 """
160 certs = self.__importCertificate()
161 if certs:
162 server = "*"
163 certificateDict = Preferences.toDict(
164 Preferences.Prefs.settings.value("Help/CaCertificatesDict"))
165 if server in certificateDict:
166 sCerts = QSslCertificate.fromData(certificateDict[server])
167 else:
168 sCerts = []
169
170 pems = QByteArray()
171 for cert in certs:
172 if cert in sCerts:
173 E5MessageBox.warning(self,
174 self.trUtf8("Import Certificate"),
175 self.trUtf8("""<p>The certificate <b>{0}</b> already exists."""
176 """ Skipping.</p>""")
177 .format(Utilities.decodeString(
178 cert.subjectInfo(QSslCertificate.CommonName))))
179 else:
180 pems.append(cert.toPem() + '\n')
181 if server not in certificateDict:
182 certificateDict[server] = QByteArray()
183 certificateDict[server].append(pems)
184 Preferences.Prefs.settings.setValue("Help/CaCertificatesDict",
185 certificateDict)
186
187 self.serversCertificatesTree.clear()
188 self.__populateServerCertificatesTree()
189
190 self.__updateDefaultConfiguration()
191
192 @pyqtSlot()
193 def on_serversExportButton_clicked(self):
194 """
195 Private slot to export the selected server certificate.
196 """
197 cert = self.serversCertificatesTree.currentItem().data(0, self.CertRole)
198 fname = self.serversCertificatesTree.currentItem().text(0)\
199 .replace(" ", "").replace("\t", "")
200 self.__exportCertificate(fname, cert)
201
202 def __updateDefaultConfiguration(self):
203 """
204 Private method to update the default SSL configuration.
205 """
206 caList = self.__getSystemCaCertificates()
207 certificateDict = Preferences.toDict(
208 Preferences.Prefs.settings.value("Help/CaCertificatesDict"))
209 for server in certificateDict:
210 for cert in QSslCertificate.fromData(certificateDict[server]):
211 if cert not in caList:
212 caList.append(cert)
213 sslCfg = QSslConfiguration.defaultConfiguration()
214 sslCfg.setCaCertificates(caList)
215 QSslConfiguration.setDefaultConfiguration(sslCfg)
216
217 def __getSystemCaCertificates(self):
218 """
219 Private method to get the list of system certificates.
220
221 @return list of system certificates (list of QSslCertificate)
222 """
223 caList = QSslCertificate.fromData(Preferences.toByteArray(
224 Preferences.Prefs.settings.value("Help/SystemCertificates")))
225 if not caList:
148 caList = QSslSocket.systemCaCertificates() 226 caList = QSslSocket.systemCaCertificates()
149 caList.extend(caNew) 227 return caList
150 sslCfg = QSslConfiguration.defaultConfiguration()
151 sslCfg.setCaCertificates(caList)
152 QSslConfiguration.setDefaultConfiguration(sslCfg)
153 228
154 def __populateCaCertificatesTree(self): 229 def __populateCaCertificatesTree(self):
155 """ 230 """
156 Private slot to populate the CA certificates tree. 231 Private slot to populate the CA certificates tree.
157 """ 232 """
158 for cert in QSslSocket.systemCaCertificates(): 233 for cert in self.__getSystemCaCertificates():
159 self.__createCaCertificateEntry(cert) 234 self.__createCaCertificateEntry(cert)
160 235
161 self.caCertificatesTree.expandAll() 236 self.caCertificatesTree.expandAll()
162 for i in range(self.caCertificatesTree.columnCount()): 237 for i in range(self.caCertificatesTree.columnCount()):
163 self.caCertificatesTree.resizeColumnToContents(i) 238 self.caCertificatesTree.resizeColumnToContents(i)
200 @param current new current item (QTreeWidgetItem) 275 @param current new current item (QTreeWidgetItem)
201 @param previous previous current item (QTreeWidgetItem) 276 @param previous previous current item (QTreeWidgetItem)
202 """ 277 """
203 enable = current is not None and current.parent() is not None 278 enable = current is not None and current.parent() is not None
204 self.caViewButton.setEnabled(enable) 279 self.caViewButton.setEnabled(enable)
280 self.caDeleteButton.setEnabled(enable)
281 self.caExportButton.setEnabled(enable)
205 282
206 @pyqtSlot() 283 @pyqtSlot()
207 def on_caViewButton_clicked(self): 284 def on_caViewButton_clicked(self):
208 """ 285 """
209 Private slot to show data of the selected CA certificate. 286 Private slot to show data of the selected CA certificate.
210 """ 287 """
211 cert = self.caCertificatesTree.currentItem().data(0, self.CertRole) 288 cert = self.caCertificatesTree.currentItem().data(0, self.CertRole)
212 dlg = SslInfoDialog(cert, self) 289 dlg = SslInfoDialog(cert, self)
213 dlg.exec_() 290 dlg.exec_()
291
292 @pyqtSlot()
293 def on_caDeleteButton_clicked(self):
294 """
295 Private slot to delete the selected CA certificate.
296 """
297 itm = self.caCertificatesTree.currentItem()
298 res = E5MessageBox.yesNo(self,
299 self.trUtf8("Delete CA Certificate"),
300 self.trUtf8("""<p>Shall the CA certificate really be deleted?</p>"""
301 """<p>{0}</p>"""
302 """<p>If the CA certificate is deleted, the browser"""
303 """ will not trust any certificate issued by this CA.</p>""")\
304 .format(itm.text(0)))
305 if res:
306 cert = self.caCertificatesTree.currentItem().data(0, self.CertRole)
307
308 # delete the selected entry and it's parent entry, if it was the only one
309 parent = itm.parent()
310 parent.takeChild(parent.indexOfChild(itm))
311 if parent.childCount() == 0:
312 self.caCertificatesTree.takeTopLevelItem(
313 self.caCertificatesTree.indexOfTopLevelItem(parent))
314
315 # delete the certificate from the CA certificate store
316 caCerts = self.__getSystemCaCertificates()
317 if cert in caCerts:
318 caCerts.remove(cert)
319 pems = QByteArray()
320 for cert in caCerts:
321 pems.append(cert.toPem() + '\n')
322 Preferences.Prefs.settings.setValue("Help/SystemCertificates", pems)
323
324 # delete the certificate from the default certificates
325 self.__updateDefaultConfiguration()
326
327 @pyqtSlot()
328 def on_caImportButton_clicked(self):
329 """
330 Private slot to import server certificates.
331 """
332 certs = self.__importCertificate()
333 if certs:
334 caCerts = self.__getSystemCaCertificates()
335 for cert in certs:
336 if cert in caCerts:
337 E5MessageBox.warning(self,
338 self.trUtf8("Import Certificate"),
339 self.trUtf8("""<p>The certificate <b>{0}</b> already exists."""
340 """ Skipping.</p>""")
341 .format(Utilities.decodeString(
342 cert.subjectInfo(QSslCertificate.CommonName))))
343 else:
344 caCerts.append(cert)
345
346 pems = QByteArray()
347 for cert in caCerts:
348 pems.append(cert.toPem() + '\n')
349 Preferences.Prefs.settings.setValue("Help/SystemCertificates", pems)
350
351 self.caCertificatesTree.clear()
352 self.__populateCaCertificatesTree()
353
354 self.__updateDefaultConfiguration()
355
356 @pyqtSlot()
357 def on_caExportButton_clicked(self):
358 """
359 Private slot to export the selected CA certificate.
360 """
361 cert = self.caCertificatesTree.currentItem().data(0, self.CertRole)
362 fname = self.caCertificatesTree.currentItem().text(0)\
363 .replace(" ", "").replace("\t", "")
364 self.__exportCertificate(fname, cert)
365
366 def __exportCertificate(self, name, cert):
367 """
368 Private slot to export a certificate.
369
370 @param name default file name without extension (string)
371 @param cert certificate to be exported (QSslCertificate)
372 """
373 if cert is not None:
374 fname, selectedFilter = QFileDialog.getSaveFileNameAndFilter(
375 self,
376 self.trUtf8("Export Certificate"),
377 name,
378 self.trUtf8("Certificate File (PEM) (*.pem);;"
379 "Certificate File (DER) (*.der)"),
380 None,
381 QFileDialog.Options(QFileDialog.DontConfirmOverwrite))
382
383 if fname:
384 ext = QFileInfo(fname).suffix()
385 if not ext or ext not in ["pem", "der"]:
386 ex = selectedFilter.split("(*")[1].split(")")[0]
387 if ex:
388 fname += ex
389 if QFileInfo(fname).exists():
390 res = E5MessageBox.yesNo(self,
391 self.trUtf8("Export Certificate"),
392 self.trUtf8("<p>The file <b>{0}</b> already exists."
393 " Overwrite it?</p>").format(fname),
394 icon = E5MessageBox.Warning)
395 if not res:
396 return
397
398 f = QFile(fname)
399 if not f.open(QIODevice.WriteOnly):
400 E5MessageBox.critical(self,
401 self.trUtf8("Export Certificate"),
402 self.trUtf8("""<p>The certificate could not be written to file"""
403 """ <b>{0}</b></p><p>Error: {1}</p>""")
404 .format(fname, f.errorString()))
405 return
406
407 if fname.endswith(".pem"):
408 crt = cert.toPem()
409 else:
410 crt = cert.toDer()
411 f.write(crt)
412 f.close()
413
414 def __importCertificate(self):
415 """
416 Private method to read a certificate.
417
418 @return certificates read (list of QSslCertificate)
419 """
420 fname, selectedFilter = QFileDialog.getOpenFileNameAndFilter(
421 self,
422 self.trUtf8("Import Certificate"),
423 "",
424 self.trUtf8("Certificate Files (*.pem *.crt *.der *.cer *.ca);;"
425 "All Files (*)"),
426 None)
427
428 if fname:
429 f = QFile(fname)
430 if not f.open(QIODevice.ReadOnly):
431 E5MessageBox.critical(self,
432 self.trUtf8("Export Certificate"),
433 self.trUtf8("""<p>The certificate could not be read from file"""
434 """ <b>{0}</b></p><p>Error: {1}</p>""")
435 .format(fname, f.errorString()))
436 return []
437
438 crt = f.readAll()
439 f.close()
440 cert = QSslCertificate.fromData(crt, QSsl.Pem)
441 if not cert:
442 cert = QSslCertificate.fromData(crt, QSsl.Der)
443 ## if fname.endswith((".pem", ".crt")):
444 ## cert = QSslCertificate.fromData(crt, QSsl.Pem)
445 ## elif fname.endswith((".der", ".cer", ".ca")):
446 ## cert = QSslCertificate.fromData(crt, QSsl.Der)
447
448 return cert
449
450 return []

eric ide

mercurial