beremiz

d3308c58845e
Parents 00ef78dffbf6
Children 3fb62fecd5c9
IDE: Add IDE identity tab in Identity Manager dialog.
--- a/CertManagement.py Thu Mar 13 13:15:05 2025 +0100
+++ b/CertManagement.py Fri Mar 14 14:27:23 2025 +0100
@@ -21,9 +21,10 @@
COL_CN, COL_DESC, COL_LAST = list(range(3))
REPLACE, KEEP, CANCEL = list(range(3))
+keystore_path = AppDataPath("keystore")
def _certpath():
- certpath = AppDataPath("keystore", "cert")
+ certpath = os.path.join(keystore_path, "cert")
return certpath
@@ -160,3 +161,45 @@
shutil.copyfile(filepath, new_filename)
_touchCN(CN)
return True
+
+
+def _clientCertPath():
+ own_keystore = os.path.join(keystore_path, "own")
+ if not os.path.exists(own_keystore):
+ os.makedirs(own_keystore)
+ return os.path.join(own_keystore, "client.crt")
+
+def GetClientCertificateInfo():
+ file_path = _clientCertPath()
+ if os.path.exists(file_path):
+ info = ""
+ try:
+ with open(file_path, "rb") as cert_file:
+ cert_data = cert_file.read()
+ cert = x509.load_pem_x509_certificate(cert_data, default_backend())
+
+ # Support for legacy common name
+ common_names = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
+ for cn in common_names:
+ info += "Common Name: %s\n"%cn.value
+ ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
+ SAN = ext.value.get_values_for_type(x509.DNSName)
+ for SANEntry in SAN:
+ info += "SubjectAltName: %s\n"%SANEntry
+
+ info += "Fingerprint: %s\n"%cert.fingerprint(hashes.SHA256()).hex()
+ info += "Creation date: %s\n"%cert.not_valid_before.isoformat()
+ info += "Expiration date: %s\n"%cert.not_valid_after.isoformat()
+ except Exception as e:
+ info += "Error while loading certificate: %s\n"%str(e)
+ return info
+ return "No client certificate available"
+
+def ImportClientCert(filepath):
+ certpath = _clientCertPath()
+ shutil.copyfile(filepath, certpath)
+
+def RemoveClientCert():
+ certpath = _clientCertPath()
+ if os.path.exists(certpath ):
+ os.remove(certpath)
--- a/PSKManagement.py Thu Mar 13 13:15:05 2025 +0100
+++ b/PSKManagement.py Fri Mar 14 14:27:23 2025 +0100
@@ -5,14 +5,16 @@
import os
-from os.path import join, exists
import time
import json
+import shutil
from zipfile import ZipFile
from binascii import b2a_base64
from util.paths import AppDataPath
+### Controllers Identities ###
+
# PSK Management Data model :
# [[ID,Desc, LastKnownURI, LastConnect]]
COL_ID, COL_URI, COL_DESC, COL_LAST = list(range(4))
@@ -171,13 +173,20 @@
return data
-def GetIDEIdentity():
+
+### IDE Identity ###
+
+def _own_psk_path():
own_keystore = AppDataPath("keystore", "own")
- if not exists(own_keystore):
+ if not os.path.exists(own_keystore):
os.makedirs(own_keystore)
- own_identity = join(own_keystore, "default.psk")
- if exists(own_identity):
+ return os.path.join(own_keystore, "default.psk")
+
+
+def GetIDEIdentity():
+ own_identity = _own_psk_path()
+ if os.path.exists(own_identity):
ID, _sep, PSK = open(own_identity).read().partition(':')
secretstring = PSK.rstrip('\n\r')
else:
@@ -192,3 +201,17 @@
f.write(PSKstring)
return ID, secretstring
+
+
+def ExportIDEIdentity(filepath):
+ own_identity = _own_psk_path()
+ shutil.copyfile(own_identity, filepath)
+
+def ImportIDEIdentity(filepath):
+ own_identity = _own_psk_path()
+ shutil.copyfile(filepath, own_identity)
+
+def RemoveIDEIdentity():
+ own_identity = _own_psk_path()
+ if os.path.exists(own_identity):
+ os.remove(own_identity)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/controls/OwnIdentityPanel.py Fri Mar 14 14:27:23 2025 +0100
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# See COPYING file for copyrights details.
+
+import os
+import wx
+import PSKManagement as PSK
+import CertManagement
+from dialogs.MsgConfirmDialog import MsgConfirmDialog
+
+
+class OwnIdentityPanel(wx.Panel):
+ def __init__(self, parent, log, **kwargs):
+ wx.Panel.__init__(self, parent, -1, size=(800, 600))
+ main_sizer = wx.BoxSizer(wx.VERTICAL)
+
+ psk_part = wx.StaticBoxSizer(wx.VERTICAL, self, "PSK")
+
+ self.psk_text = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.BORDER_NONE)
+
+ self.RefreshPSKText()
+
+ psk_part.Add(self.psk_text, 1, border=5, flag=wx.GROW | wx.ALL)
+
+ psk_btn_box = wx.BoxSizer(wx.HORIZONTAL)
+
+ exportButton = wx.Button(self, label=_("Export PSK"))
+ self.Bind(wx.EVT_BUTTON, self.OnPSKExportButton, exportButton)
+ psk_btn_box.Add(exportButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+ importButton = wx.Button(self, label=_("Import PSK"))
+ self.Bind(wx.EVT_BUTTON, self.OnPSKImportButton, importButton)
+ psk_btn_box.Add(importButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+ regenButton = wx.Button(self, label=_("Regen PSK"))
+ self.Bind(wx.EVT_BUTTON, self.OnPSKRegenButton, regenButton)
+ psk_btn_box.Add(regenButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+ psk_part.Add(psk_btn_box, 0, border=5, flag=wx.GROW | wx.ALL)
+ main_sizer.Add(psk_part, 1, border=5, flag=wx.GROW | wx.ALL)
+
+ cert_part = wx.StaticBoxSizer(wx.VERTICAL, self, "Client Certificate")
+
+ self.cert_text = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.BORDER_NONE)
+
+ self.RefreshCertText()
+
+ cert_part.Add(self.cert_text, 1, border=5, flag=wx.GROW | wx.ALL)
+
+ cert_btn_box = wx.BoxSizer(wx.HORIZONTAL)
+
+ importClientCertButton = wx.Button(self, label=_("Import certificate"))
+ self.Bind(wx.EVT_BUTTON, self.OnImportClientCertButton, importClientCertButton)
+ cert_btn_box.Add(importClientCertButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+ deleteClientCertButton = wx.Button(self, label=_("Delete certificate"))
+ self.Bind(wx.EVT_BUTTON, self.OnDeleteClientCertButton, deleteClientCertButton)
+ cert_btn_box.Add(deleteClientCertButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+ cert_part.Add(cert_btn_box, 0, border=5, flag=wx.GROW | wx.ALL)
+ main_sizer.Add(cert_part, 1, border=5, flag=wx.GROW | wx.ALL)
+
+ self.SetSizer(main_sizer)
+
+
+ ### PSK ###
+
+ def OnPSKExportButton(self, event):
+ dialog = wx.FileDialog(self, _("Export IDE PSK"),
+ wildcard=_("PLCOpen files (*.psk)|*.psk|All files|*.*"),
+ style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
+
+ if dialog.ShowModal() == wx.ID_OK:
+ filepath = dialog.GetPath()
+ error = None
+
+ if os.path.isdir(os.path.dirname(filepath)):
+ try:
+ PSK.ExportIDEIdentity(filepath)
+ except Exception as e:
+ error = _("Can't save PSK to file %s. Error: %s") % filepath
+ else:
+ error = _("\"%s\" is not a valid folder!") % os.path.dirname(filepath)
+
+ if error:
+ dlg = wx.MessageDialog(self, error, _("Error"), wx.OK | wx.ICON_ERROR)
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ dialog.Destroy()
+
+ def _confirm_overwrite_identity(self):
+ dialog = wx.MessageDialog(self,
+ _("This will replace IDE Identity. Confirm ?"),
+ _("Warning"), wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
+ answer = dialog.ShowModal()
+ dialog.Destroy()
+ return answer == wx.ID_YES
+
+ def OnPSKImportButton(self, event):
+ dialog = wx.FileDialog(self, _("Choose a file"),
+ wildcard=_("PSK files (*.psk)|*.psk|All files|*.*"),
+ style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
+ if dialog.ShowModal() == wx.ID_OK:
+ if self._confirm_overwrite_identity():
+ PSK.ImportIDEIdentity(dialog.GetPath())
+ self.RefreshPSKText()
+
+ def OnPSKRegenButton(self, event):
+ if self._confirm_overwrite_identity():
+ PSK.RemoveIDEIdentity()
+ self.RefreshPSKText()
+
+ def RefreshPSKText(self):
+ try:
+ IDE_ID, secret = PSK.GetIDEIdentity()
+ text = "Current ID: " + IDE_ID
+ except Exception as e:
+ text = "Failed retrieving IDE ID: " + str(e)
+
+ self.psk_text.SetValue(text)
+
+ ### Certificates ###
+
+ def OnImportClientCertButton(self, event):
+ dialog = wx.FileDialog(self, _("Choose a file"),
+ wildcard=_("Certificate files (*.crt)|*.crt|All files|*.*"),
+ style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
+ if dialog.ShowModal() == wx.ID_OK:
+ if self._confirm_overwrite_identity():
+ CertManagement.ImportClientCert(dialog.GetPath())
+ self.RefreshCertText()
+
+ def OnDeleteClientCertButton(self, event):
+ if self._confirm_overwrite_identity():
+ CertManagement.RemoveClientCert()
+ self.RefreshCertText()
+
+ def RefreshCertText(self):
+ text = CertManagement.GetClientCertificateInfo()
+ self.cert_text.SetValue(text)
+
--- a/dialogs/IDManager.py Thu Mar 13 13:15:05 2025 +0100
+++ b/dialogs/IDManager.py Fri Mar 14 14:27:23 2025 +0100
@@ -3,6 +3,8 @@
import wx
from controls.IDBrowser import IDBrowser
from controls.CertBrowser import CertBrowser
+from controls.OwnIdentityPanel import OwnIdentityPanel
+
class IDManageNB(wx.Notebook):
def __init__(self, parent, ctr):
@@ -19,7 +21,7 @@
self.id_browser = IDBrowser(self, ctr)
self.AddPage(self.id_browser, "Controllers")
- win = wx.Panel(self, -1)
+ win = OwnIdentityPanel(self, -1)
self.AddPage(win, "IDE")
self.cert_browser = CertBrowser(self, ctr.logger)