From 216dd2a787e2aa324653e4128a21a8cd67ac285b Mon Sep 17 00:00:00 2001 From: hz2016 Date: Thu, 28 Apr 2022 15:58:33 +0800 Subject: [PATCH 01/10] Update the SSO Login for Serenity and Singularity server's player --- config.py | 4 ++ .../pyfaEsiPreferences.py | 29 +++++++++++++- gui/ssoLogin.py | 13 +++++++ service/esi.py | 11 +++++- service/esiAccess.py | 38 ++++++++++++------- service/settings.py | 3 ++ 6 files changed, 82 insertions(+), 16 deletions(-) diff --git a/config.py b/config.py index 08d6411956..1855b96fc2 100644 --- a/config.py +++ b/config.py @@ -44,8 +44,12 @@ language = None API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746' +API_CLIENT_ID_SERENITY = 'bc90aa496a404724a93f41b4f4e97761' + ESI_CACHE = 'esi_cache' SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback' +SSO_CALLBACK_SERENITY='https://esi.evepc.163.com/ui/oauth2-redirect.html' +SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff' LOGLEVEL_MAP = { "critical": CRITICAL, diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py index 1356fec16c..f769cbe6ed 100644 --- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -48,9 +48,30 @@ def populatePanel(self, panel): self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) - mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) + mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0) + + esiSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition,wx.DefaultSize, 0) + + self.esiServer.Wrap(-1) + + esiSizer.Add(self.esiServer, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + self.esiServer.SetToolTip(wx.ToolTip(_t('The source you choose will be used on connection.'))) + + self.chESIserver = wx.Choice(panel, choices=list(self.settings.keys())) + + self.chESIserver.SetStringSelection(self.settings.get("server")) + + esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10) + + mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT , 10) + + self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange) panel.SetSizer(mainSizer) + panel.Layout() def OnTimeoutChange(self, event): @@ -59,8 +80,12 @@ def OnTimeoutChange(self, event): def OnModeChange(self, event): self.settings.set('loginMode', event.GetInt()) + def OnServerChange(self, event): + source = self.chESIserver.GetString(self.chESIserver.GetSelection()) + self.settings.set("server",source) + def getImage(self): return BitmapLoader.getBitmap("eve", "gui") -PFEsiPref.register() +PFEsiPref.register() \ No newline at end of file diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py index 28f283cd8f..9a3eac1b57 100644 --- a/gui/ssoLogin.py +++ b/gui/ssoLogin.py @@ -26,6 +26,18 @@ def __init__(self): bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) + from service.settings import EsiSettings + import config + import time + self.Esisettings = EsiSettings.getInstance() + if (self.Esisettings.get("server") == "Serenity"): + bSizer4 = wx.BoxSizer(wx.VERTICAL) + text = wx.StaticText(self, wx.ID_ANY, _t("Please copy the url when your authorization is completed")) + bSizer4.Add(text, 0, wx.ALL | wx.EXPAND, 10) + bSizer1.Add(bSizer4, 0, wx.ALL | wx.EXPAND, 10) + webbrowser.open(config.SSO_LOGOFF_SERENITY) + time.sleep(1) + bSizer3 = wx.BoxSizer(wx.VERTICAL) bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10) @@ -39,6 +51,7 @@ def __init__(self): self.sEsi = Esi.getInstance() uri = self.sEsi.get_login_uri(None) + webbrowser.open(uri) diff --git a/service/esi.py b/service/esi.py index 3fb38ac034..bc0ef14a8d 100644 --- a/service/esi.py +++ b/service/esi.py @@ -108,7 +108,16 @@ def login(self): else: with gui.ssoLogin.SsoLogin() as dlg: if dlg.ShowModal() == wx.ID_OK: - message = json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip())) + message = {} + if (self.server_name == "Serenity"): + import re + s=re.search(r'(?<=code=)[a-zA-Z0-9\-_]*',dlg.ssoInfoCtrl.Value.strip()) + if s: + message['code']=s.group() + else: + message['code']=None + else: + message = json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip())) self.handleLogin(message) def stopServer(self): diff --git a/service/esiAccess.py b/service/esiAccess.py index 12bcddc948..984059315d 100644 --- a/service/esiAccess.py +++ b/service/esiAccess.py @@ -65,7 +65,8 @@ def __str__(self): class EsiAccess: def __init__(self): self.settings = EsiSettings.getInstance() - self.server_base: ApiBase = supported_servers[self.settings.get("server")] + self.server_name=self.settings.get('server') + self.server_base: ApiBase = supported_servers[self.server_name] # session request stuff self._session = Session() @@ -116,7 +117,10 @@ def oauth_token(self): @property def client_id(self): - return self.settings.get('clientID') or config.API_CLIENT_ID + if (self.server_name == "Serenity"): + return self.settings.get('clientID') or config.API_CLIENT_ID + else: + return self.settings.get('clientID') or config.API_CLIENT_ID_SERENITY @staticmethod def update_token(char, tokenResponse): @@ -141,17 +145,25 @@ def get_login_uri(self, redirect=None): 'redirect': redirect, 'state': self.state } - - args = { - 'response_type': 'code', - 'redirect_uri': config.SSO_CALLBACK, - 'client_id': self.client_id, - 'scope': ' '.join(scopes), - 'code_challenge': code_challenge, - 'code_challenge_method': 'S256', - 'state': base64.b64encode(bytes(json.dumps(state_arg), 'utf-8')) - } - + if(self.server_name=="Serenity"): + args = { + 'response_type': 'code', + 'redirect_uri': config.SSO_CALLBACK_SERENITY, + 'client_id': self.client_id, + 'scope': ' '.join(scopes), + 'state': 'hilltech', + 'device_id': 'eims' + } + else: + args = { + 'response_type': 'code', + 'redirect_uri': config.SSO_CALLBACK, + 'client_id': self.client_id, + 'scope': ' '.join(scopes), + 'code_challenge': code_challenge, + 'code_challenge_method': 'S256', + 'state': base64.b64encode(bytes(json.dumps(state_arg), 'utf-8')) + } return '%s?%s' % ( self.oauth_authorize, urlencode(args) diff --git a/service/settings.py b/service/settings.py index 64435cd4db..36be2b6c52 100644 --- a/service/settings.py +++ b/service/settings.py @@ -388,6 +388,9 @@ def get(self, type): def set(self, type, value): self.settings[type] = value + def keys(self): + return list({"Tranquility":"Tranquility","Singularity":"Singularity","Serenity":"Serenity"}) + class StatViewSettings: _instance = None From 14afc83e86403efd8b311598eaa11eb1f37aba6a Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 30 Apr 2022 10:17:51 -0400 Subject: [PATCH 02/10] Fix client ID that is used --- service/esiAccess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/esiAccess.py b/service/esiAccess.py index 984059315d..4441a5fe39 100644 --- a/service/esiAccess.py +++ b/service/esiAccess.py @@ -118,9 +118,9 @@ def oauth_token(self): @property def client_id(self): if (self.server_name == "Serenity"): - return self.settings.get('clientID') or config.API_CLIENT_ID - else: return self.settings.get('clientID') or config.API_CLIENT_ID_SERENITY + else: + return self.settings.get('clientID') or config.API_CLIENT_ID @staticmethod def update_token(char, tokenResponse): From dc997f0dc4ace69daa0d7cdb44a9d86f82738c14 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 7 May 2022 12:35:16 -0400 Subject: [PATCH 03/10] Various updates to support server-aware calls per character --- config.py | 14 ++++-- eos/db/migrations/upgrade47.py | 19 +++++++ eos/db/saveddata/character.py | 1 + eos/db/saveddata/queries.py | 5 +- eos/saveddata/ssocharacter.py | 6 ++- .../pyfaEsiPreferences.py | 6 ++- gui/characterEditor.py | 2 +- gui/esiFittings.py | 7 ++- service/esi.py | 10 ++-- service/esiAccess.py | 50 +++++++++---------- service/settings.py | 2 +- 11 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 eos/db/migrations/upgrade47.py diff --git a/config.py b/config.py index 1855b96fc2..91622b5fb8 100644 --- a/config.py +++ b/config.py @@ -9,6 +9,7 @@ from eos.const import FittingSlot from cryptography.fernet import Fernet +from collections import namedtuple pyfalog = Logger(__name__) @@ -43,13 +44,16 @@ version = None language = None -API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746' -API_CLIENT_ID_SERENITY = 'bc90aa496a404724a93f41b4f4e97761' +ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback']) +supported_servers = { + "Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback'), + # No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity + # "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'), + "Serenity": ApiServer("Serenity", "login.evepc.163.com", "esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://esi.evepc.163.com/ui/oauth2-redirect.html') +} -ESI_CACHE = 'esi_cache' -SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback' -SSO_CALLBACK_SERENITY='https://esi.evepc.163.com/ui/oauth2-redirect.html' SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff' +ESI_CACHE = 'esi_cache' LOGLEVEL_MAP = { "critical": CRITICAL, diff --git a/eos/db/migrations/upgrade47.py b/eos/db/migrations/upgrade47.py new file mode 100644 index 0000000000..7aa5d5ca56 --- /dev/null +++ b/eos/db/migrations/upgrade47.py @@ -0,0 +1,19 @@ +""" +Migration 28 + +- adds baseItemID and mutaplasmidID to modules table +""" +import sqlalchemy + + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT server FROM ssoCharacter LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE ssoCharacter ADD COLUMN server VARCHAR;") + saveddata_engine.execute("UPDATE ssoCharacter SET server = 'Tranquility';") + + + + # update all characters to TQ diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index 4dd84cf419..0c878b0668 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -44,6 +44,7 @@ Column("client", String, nullable=False), Column("characterID", Integer, nullable=False), Column("characterName", String, nullable=False), + Column("server", String, nullable=False), Column("refreshToken", String, nullable=False), Column("accessToken", String, nullable=False), Column("accessTokenExpires", DateTime, nullable=False), diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index f8b5f79bdd..4d4ad8f5c5 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -493,9 +493,12 @@ def getSsoCharacters(clientHash, eager=None): @cachedQuery(SsoCharacter, 1, "lookfor", "clientHash") -def getSsoCharacter(lookfor, clientHash, eager=None): +def getSsoCharacter(lookfor, clientHash, server=None, eager=None): filter = SsoCharacter.client == clientHash + if server is not None: + filter = and_(filter, SsoCharacter.server == server) + if isinstance(lookfor, int): filter = and_(filter, SsoCharacter.ID == lookfor) elif isinstance(lookfor, str): diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py index 49b742442c..6cbc292193 100644 --- a/eos/saveddata/ssocharacter.py +++ b/eos/saveddata/ssocharacter.py @@ -25,10 +25,11 @@ class SsoCharacter: - def __init__(self, charID, name, client, accessToken=None, refreshToken=None): + def __init__(self, charID, name, client, server, accessToken=None, refreshToken=None): self.characterID = charID self.characterName = name self.client = client + self.server = server self.accessToken = accessToken self.refreshToken = refreshToken self.accessTokenExpires = None @@ -37,6 +38,9 @@ def __init__(self, charID, name, client, accessToken=None, refreshToken=None): def init(self): pass + @property + def characterDisplay(self): + return "{} [{}]".format(self.characterName, self.server) def is_token_expired(self): if self.accessTokenExpires is None: return True diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py index f769cbe6ed..359a871c1a 100644 --- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -1,9 +1,11 @@ # noinspection PyPackageRequirements import wx +import config import gui.mainFrame from gui.bitmap_loader import BitmapLoader from gui.preferenceView import PreferenceView +from service.esi import Esi from service.settings import EsiSettings # noinspection PyPackageRequirements @@ -82,7 +84,9 @@ def OnModeChange(self, event): def OnServerChange(self, event): source = self.chESIserver.GetString(self.chESIserver.GetSelection()) - self.settings.set("server",source) + esiService = Esi.getInstance() + esiService.init(config.supported_servers[source]) + self.settings.set("server", source) def getImage(self): return BitmapLoader.getBitmap("eve", "gui") diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 9f268556d3..4a698ae912 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -856,7 +856,7 @@ def charChanged(self, event): noneID = self.charChoice.Append(_t("None"), None) for char in ssoChars: - currId = self.charChoice.Append(char.characterName, char.ID) + currId = self.charChoice.Append(char.characterDisplay, char.ID) if sso is not None and char.ID == sso.ID: self.charChoice.SetSelection(currId) diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 0f99508801..467776f92b 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -96,7 +96,7 @@ def updateCharList(self): self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.characterName, char.ID) + self.charChoice.Append(char.characterDisplay, char.ID) if len(chars) > 0: self.charChoice.SetSelection(0) @@ -330,7 +330,7 @@ def updateCharList(self): self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.characterName, char.ID) + self.charChoice.Append(char.characterDisplay, char.ID) if len(chars) > 0: self.charChoice.SetSelection(0) @@ -414,6 +414,7 @@ def __init__(self, parent): self.lcCharacters.InsertColumn(0, heading=_t('Character')) self.lcCharacters.InsertColumn(1, heading=_t('Character ID')) + self.lcCharacters.InsertColumn(2, heading=_t('Server')) self.popCharList() @@ -476,9 +477,11 @@ def popCharList(self): self.lcCharacters.InsertItem(index, char.characterName) self.lcCharacters.SetItem(index, 1, str(char.characterID)) self.lcCharacters.SetItemData(index, char.ID) + self.lcCharacters.SetItem(index, 2, char.server or "") self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) + self.lcCharacters.SetColumnWidth(2, wx.LIST_AUTOSIZE) def addChar(self, event): try: diff --git a/service/esi.py b/service/esi.py index bc0ef14a8d..279f65dc35 100644 --- a/service/esi.py +++ b/service/esi.py @@ -69,8 +69,8 @@ def getSsoCharacters(self): chars = eos.db.getSsoCharacters(config.getClientSecret()) return chars - def getSsoCharacter(self, id): - char = eos.db.getSsoCharacter(id, config.getClientSecret()) + def getSsoCharacter(self, id, server=None): + char = eos.db.getSsoCharacter(id, config.getClientSecret(), server) eos.db.commit() return char @@ -109,7 +109,7 @@ def login(self): with gui.ssoLogin.SsoLogin() as dlg: if dlg.ShowModal() == wx.ID_OK: message = {} - if (self.server_name == "Serenity"): + if (self.default_server_name == "Serenity"): import re s=re.search(r'(?<=code=)[a-zA-Z0-9\-_]*',dlg.ssoInfoCtrl.Value.strip()) if s: @@ -146,14 +146,14 @@ def startServer(self, port): # todo: break this out into two functions: startin def handleLogin(self, message): auth_response, data = self.auth(message['code']) - currentCharacter = self.getSsoCharacter(data['name']) + currentCharacter = self.getSsoCharacter(data['name'], self.server_base.name) sub_split = data["sub"].split(":") if (len(sub_split) != 3): raise GenericSsoError("JWT sub does not contain the expected data. Contents: %s" % data["sub"]) cid = sub_split[-1] if currentCharacter is None: - currentCharacter = SsoCharacter(cid, data['name'], config.getClientSecret()) + currentCharacter = SsoCharacter(cid, data['name'], config.getClientSecret(), self.server_base.name) Esi.update_token(currentCharacter, auth_response) diff --git a/service/esiAccess.py b/service/esiAccess.py index 4441a5fe39..ffd22fa624 100644 --- a/service/esiAccess.py +++ b/service/esiAccess.py @@ -30,13 +30,6 @@ 'esi-fittings.write_fittings.v1' ] -ApiBase = namedtuple('ApiBase', ['sso', 'esi']) -supported_servers = { - "Tranquility": ApiBase("login.eveonline.com", "esi.evetech.net"), - "Singularity": ApiBase("sisilogin.testeveonline.com", "esi.evetech.net"), - "Serenity": ApiBase("login.evepc.163.com", "esi.evepc.163.com") -} - class GenericSsoError(Exception): """ Exception used for generic SSO errors that aren't directly related to an API call """ @@ -63,11 +56,11 @@ def __str__(self): class EsiAccess: + server_meta = {} def __init__(self): self.settings = EsiSettings.getInstance() - self.server_name=self.settings.get('server') - self.server_base: ApiBase = supported_servers[self.server_name] - + self.default_server_name = self.settings.get('server') + self.default_server_base = config.supported_servers[self.default_server_name] # session request stuff self._session = Session() self._basicHeaders = { @@ -81,21 +74,25 @@ def __init__(self): # Set up cached session. This is only used for SSO meta data for now, but can be expanded to actually handle # various ESI caching (using ETag, for example) in the future - cached_session = CachedSession( + self.cached_session = CachedSession( os.path.join(config.savePath, config.ESI_CACHE), backend="sqlite", cache_control=True, # Use Cache-Control headers for expiration, if available expire_after=timedelta(days=1), # Otherwise expire responses after one day stale_if_error=True, # In case of request errors, use stale cache data if possible ) - cached_session.headers.update(self._basicHeaders) - cached_session.proxies = NetworkSettings.getInstance().getProxySettingsInRequestsFormat() - - meta_call = cached_session.get("https://%s/.well-known/oauth-authorization-server" % self.server_base.sso) + self.cached_session.headers.update(self._basicHeaders) + self.cached_session.proxies = NetworkSettings.getInstance().getProxySettingsInRequestsFormat() + self.init(self.default_server_base) + + def init(self, server_base): + self.server_base: config.ApiServer = server_base + self.server_name = self.server_base.name + meta_call = self.cached_session.get("https://%s/.well-known/oauth-authorization-server" % self.server_base.sso) meta_call.raise_for_status() self.server_meta = meta_call.json() - jwks_call = cached_session.get(self.server_meta["jwks_uri"]) + jwks_call = self.cached_session.get(self.server_meta["jwks_uri"]) jwks_call.raise_for_status() self.jwks = jwks_call.json() @@ -117,10 +114,7 @@ def oauth_token(self): @property def client_id(self): - if (self.server_name == "Serenity"): - return self.settings.get('clientID') or config.API_CLIENT_ID_SERENITY - else: - return self.settings.get('clientID') or config.API_CLIENT_ID + return self.settings.get('clientID') or self.server_base.client_id @staticmethod def update_token(char, tokenResponse): @@ -145,10 +139,11 @@ def get_login_uri(self, redirect=None): 'redirect': redirect, 'state': self.state } + if(self.server_name=="Serenity"): args = { 'response_type': 'code', - 'redirect_uri': config.SSO_CALLBACK_SERENITY, + 'redirect_uri': self.server_base.callback, 'client_id': self.client_id, 'scope': ' '.join(scopes), 'state': 'hilltech', @@ -157,7 +152,7 @@ def get_login_uri(self, redirect=None): else: args = { 'response_type': 'code', - 'redirect_uri': config.SSO_CALLBACK, + 'redirect_uri': self.server_base.callback, 'client_id': self.client_id, 'scope': ' '.join(scopes), 'code_challenge': code_challenge, @@ -264,6 +259,11 @@ def validate_eve_jwt(self, jwt_token): "https://login.eveonline.com: {}".format(str(e))) def _before_request(self, ssoChar): + if ssoChar: + self.init(config.supported_servers[ssoChar.server]) + else: + self.init(self.default_server_base) + self._session.headers.clear() self._session.headers.update(self._basicHeaders) if ssoChar is None: @@ -292,17 +292,17 @@ def _after_request(self, resp): def get(self, ssoChar, endpoint, **kwargs): self._before_request(ssoChar) endpoint = endpoint.format(**kwargs) - return self._after_request(self._session.get("{}{}".format(self.esi_url, endpoint))) + return self._after_request(self._session.get("{}{}?datasource={}".format(self.esi_url, endpoint, self.server_name.lower()))) def post(self, ssoChar, endpoint, json, **kwargs): self._before_request(ssoChar) endpoint = endpoint.format(**kwargs) - return self._after_request(self._session.post("{}{}".format(self.esi_url, endpoint), data=json)) + return self._after_request(self._session.post("{}{}?datasource={}".format(self.esi_url, endpoint, self.server_name.lower()), data=json)) def delete(self, ssoChar, endpoint, **kwargs): self._before_request(ssoChar) endpoint = endpoint.format(**kwargs) - return self._after_request(self._session.delete("{}{}".format(self.esi_url, endpoint))) + return self._after_request(self._session.delete("{}{}?datasource={}".format(self.esi_url, endpoint, self.server_name.lower()))) # todo: move these off to another class which extends this one. This class should only handle the low level # authentication and diff --git a/service/settings.py b/service/settings.py index 36be2b6c52..c1e91f9dd3 100644 --- a/service/settings.py +++ b/service/settings.py @@ -389,7 +389,7 @@ def set(self, type, value): self.settings[type] = value def keys(self): - return list({"Tranquility":"Tranquility","Singularity":"Singularity","Serenity":"Serenity"}) + return config.supported_servers.keys() class StatViewSettings: From 3c6eb6d05470ff2fe8fdd514f21470cf4688c226 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 8 May 2022 12:24:01 -0400 Subject: [PATCH 04/10] Some updates to allow code / token paste regardless of server setting --- config.py | 6 ++-- gui/esiFittings.py | 2 -- gui/ssoLogin.py | 88 ++++++++++++++++++++-------------------------- service/esi.py | 48 ++++++++++++------------- 4 files changed, 64 insertions(+), 80 deletions(-) diff --git a/config.py b/config.py index 91622b5fb8..fa28ef0e1d 100644 --- a/config.py +++ b/config.py @@ -44,12 +44,12 @@ version = None language = None -ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback']) +ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback', 'supports_auto_login']) supported_servers = { - "Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback'), + "Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True), # No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity # "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'), - "Serenity": ApiServer("Serenity", "login.evepc.163.com", "esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://esi.evepc.163.com/ui/oauth2-redirect.html') + "Serenity": ApiServer("Serenity", "login.evepc.163.com", "esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://esi.evepc.163.com/ui/oauth2-redirect.html', False) } SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff' diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 467776f92b..34ad423848 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -489,8 +489,6 @@ def addChar(self, event): sEsi.login() except (KeyboardInterrupt, SystemExit): raise - except Exception as ex: - ESIServerExceptionHandler(self, ex) def delChar(self, event): item = self.lcCharacters.GetFirstSelected() diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py index 9a3eac1b57..a1e3d2a165 100644 --- a/gui/ssoLogin.py +++ b/gui/ssoLogin.py @@ -2,41 +2,47 @@ import gui.mainFrame import webbrowser import gui.globalEvents as GE +import config +import time + +from service.settings import EsiSettings _t = wx.GetTranslation class SsoLogin(wx.Dialog): - def __init__(self): - mainFrame = gui.mainFrame.MainFrame.getInstance() - + def __init__(self, server: config.ApiServer, start_local_server=True): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + from service.esi import Esi super().__init__( - mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE, + self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE, size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240)) bSizer1 = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText(self, wx.ID_ANY, _t("Copy and paste the block of text provided by pyfa.io")) - bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) + if start_local_server: + text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On.")) + bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) + bSizer1.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.EXPAND, 15) + text = wx.StaticText(self, wx.ID_ANY, _t("If auto-login fails, copy and paste the token provided by pyfa.io")) + bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) + elif server.name == "Serenity": + text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the url when your authorization is completed")) + bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) + + else: + text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the token provided by pyfa.io")) + bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE) self.ssoInfoCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) self.ssoInfoCtrl.Layout() + self.ssoInfoCtrl.Bind(wx.EVT_TEXT, self.OnTextEnter) bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) - from service.settings import EsiSettings - import config - import time self.Esisettings = EsiSettings.getInstance() - if (self.Esisettings.get("server") == "Serenity"): - bSizer4 = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText(self, wx.ID_ANY, _t("Please copy the url when your authorization is completed")) - bSizer4.Add(text, 0, wx.ALL | wx.EXPAND, 10) - bSizer1.Add(bSizer4, 0, wx.ALL | wx.EXPAND, 10) - webbrowser.open(config.SSO_LOGOFF_SERENITY) - time.sleep(1) bSizer3 = wx.BoxSizer(wx.VERTICAL) bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10) @@ -46,49 +52,31 @@ def __init__(self): self.SetSizer(bSizer1) self.Center() - - from service.esi import Esi - self.sEsi = Esi.getInstance() - uri = self.sEsi.get_login_uri(None) - - webbrowser.open(uri) - - -class SsoLoginServer(wx.Dialog): - - def __init__(self, port): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - super().__init__(self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) - - from service.esi import Esi - - self.sEsi = Esi.getInstance() - serverAddr = self.sEsi.startServer(port) + serverAddr = self.sEsi.startServer(0) if start_local_server else None uri = self.sEsi.get_login_uri(serverAddr) - bSizer1 = wx.BoxSizer(wx.VERTICAL) + if server.name == "Serenity": + webbrowser.open(config.SSO_LOGOFF_SERENITY) + time.sleep(1) + self.okBtn = self.FindWindow(wx.ID_OK) + webbrowser.open(uri) + self.okBtn.Enable(False) + self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) - text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On.")) - bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) - - bSizer3 = wx.BoxSizer(wx.VERTICAL) - bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10) - - bSizer3.Add(self.CreateStdDialogButtonSizer(wx.CANCEL), 0, wx.EXPAND) - bSizer1.Add(bSizer3, 0, wx.BOTTOM | wx.RIGHT | wx.LEFT | wx.EXPAND, 10) - - self.SetSizer(bSizer1) - self.Fit() - self.Center() - - webbrowser.open(uri) + def OnTextEnter(self, event): + t = event.String.strip() + if t == "": + self.okBtn.Enable(False) + else: + self.okBtn.Enable(True) + event.Skip() def OnLogin(self, event): - self.EndModal(wx.ID_OK) + self.EndModal(wx.ID_CANCEL) event.Skip() def OnDestroy(self, event): diff --git a/service/esi.py b/service/esi.py index 279f65dc35..606dd9742a 100644 --- a/service/esi.py +++ b/service/esi.py @@ -6,14 +6,14 @@ import base64 import json import config -import webbrowser +import re import eos.db from service.const import EsiLoginMethod, EsiSsoMode from eos.saveddata.ssocharacter import SsoCharacter from service.esiAccess import APIException, GenericSsoError import gui.globalEvents as GE -from gui.ssoLogin import SsoLogin, SsoLoginServer +from gui.ssoLogin import SsoLogin from service.server import StoppableHTTPServer, AuthHandler from service.settings import EsiSettings from service.esiAccess import EsiAccess @@ -101,24 +101,20 @@ def delFitting(self, id, fittingID): self.fittings_deleted.add(fittingID) def login(self): - # always start the local server if user is using client details. Otherwise, start only if they choose to do so. - if self.settings.get('loginMode') == EsiLoginMethod.SERVER: - with gui.ssoLogin.SsoLoginServer(0) as dlg: - dlg.ShowModal() - else: - with gui.ssoLogin.SsoLogin() as dlg: - if dlg.ShowModal() == wx.ID_OK: - message = {} - if (self.default_server_name == "Serenity"): - import re - s=re.search(r'(?<=code=)[a-zA-Z0-9\-_]*',dlg.ssoInfoCtrl.Value.strip()) - if s: - message['code']=s.group() - else: - message['code']=None + start_server = self.settings.get('loginMode') == EsiLoginMethod.SERVER and self.server_base.supports_auto_login + with gui.ssoLogin.SsoLogin(self.server_base, start_server) as dlg: + if dlg.ShowModal() == wx.ID_OK: + if self.default_server_name == "Serenity": + s = re.search(r'(?<=code=)[a-zA-Z0-9\-_]*', dlg.ssoInfoCtrl.Value.strip()) + if s: + # skip state verification and go directly through the auth code processing + self.handleLogin(s.group) else: - message = json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip())) - self.handleLogin(message) + pass + # todo: throw error + else: + self.handleServerRequest(json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip()))) + def stopServer(self): pyfalog.debug("Stopping Server") @@ -136,21 +132,23 @@ def startServer(self, port): # todo: break this out into two functions: startin self.httpd = StoppableHTTPServer(('localhost', port), AuthHandler) port = self.httpd.socket.getsockname()[1] - self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleServerLogin,)) + self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleServerRequest,)) self.serverThread.name = "SsoCallbackServer" self.serverThread.daemon = True self.serverThread.start() return 'http://localhost:{}'.format(port) - def handleLogin(self, message): - auth_response, data = self.auth(message['code']) + def handleLogin(self, code): + auth_response, data = self.auth(code) currentCharacter = self.getSsoCharacter(data['name'], self.server_base.name) sub_split = data["sub"].split(":") - if (len(sub_split) != 3): + + if len(sub_split) != 3: raise GenericSsoError("JWT sub does not contain the expected data. Contents: %s" % data["sub"]) + cid = sub_split[-1] if currentCharacter is None: currentCharacter = SsoCharacter(cid, data['name'], config.getClientSecret(), self.server_base.name) @@ -162,7 +160,7 @@ def handleLogin(self, message): # get (endpoint, char, data?) - def handleServerLogin(self, message): + def handleServerRequest(self, message): if not message: raise GenericSsoError("Could not parse out querystring parameters.") @@ -178,4 +176,4 @@ def handleServerLogin(self, message): pyfalog.debug("Handling SSO login with: {0}", message) - self.handleLogin(message) + self.handleLogin(message['code']) From ed48a8b5d0578d331ad73951ea883b44dedfce6e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 8 May 2022 13:21:23 -0400 Subject: [PATCH 05/10] Re-work the exceptions related to logging a character in --- gui/esiFittings.py | 15 --------------- gui/ssoLogin.py | 10 +++++++++- service/esi.py | 35 ++++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 34ad423848..6b6ca4d362 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -227,21 +227,6 @@ def deleteAllFittings(self, event): self.fitView.update([]) -class ESIServerExceptionHandler: - def __init__(self, parentWindow, ex): - pyfalog.error(ex) - with wx.MessageDialog( - parentWindow, - _t("There was an issue starting up the localized server, try setting " - "Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> " - "Login Authentication Method. If this doesn't fix the problem please file an " - "issue on Github."), - _t("Add Character Error"), - wx.OK | wx.ICON_ERROR - ) as dlg: - dlg.ShowModal() - - class ESIExceptionHandler: # todo: make this a generate excetpion handler for all calls def __init__(self, ex): diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py index a1e3d2a165..cce7615ff1 100644 --- a/gui/ssoLogin.py +++ b/gui/ssoLogin.py @@ -60,11 +60,16 @@ def __init__(self, server: config.ApiServer, start_local_server=True): if server.name == "Serenity": webbrowser.open(config.SSO_LOGOFF_SERENITY) time.sleep(1) + self.okBtn = self.FindWindow(wx.ID_OK) - webbrowser.open(uri) self.okBtn.Enable(False) + # Ensure we clean up once they hit the "OK" button + self.okBtn.Bind(wx.EVT_BUTTON, self.OnDestroy) + + webbrowser.open(uri) self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin) + # Ensure we clean up if ESC is pressed self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) def OnTextEnter(self, event): @@ -76,10 +81,13 @@ def OnTextEnter(self, event): event.Skip() def OnLogin(self, event): + # This would normally happen if it was logged in via server auto-login. In this case, the modal is done, we effectively want to cancel out self.EndModal(wx.ID_CANCEL) event.Skip() def OnDestroy(self, event): + # Clean up by unbinding some events and stopping the server self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin) + self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy) self.sEsi.stopServer() event.Skip() diff --git a/service/esi.py b/service/esi.py index 606dd9742a..1bfa103b76 100644 --- a/service/esi.py +++ b/service/esi.py @@ -22,6 +22,7 @@ from requests import Session pyfalog = Logger(__name__) +_t = wx.GetTranslation class Esi(EsiAccess): @@ -104,16 +105,32 @@ def login(self): start_server = self.settings.get('loginMode') == EsiLoginMethod.SERVER and self.server_base.supports_auto_login with gui.ssoLogin.SsoLogin(self.server_base, start_server) as dlg: if dlg.ShowModal() == wx.ID_OK: - if self.default_server_name == "Serenity": - s = re.search(r'(?<=code=)[a-zA-Z0-9\-_]*', dlg.ssoInfoCtrl.Value.strip()) - if s: - # skip state verification and go directly through the auth code processing - self.handleLogin(s.group) + from gui.esiFittings import ESIExceptionHandler + + try: + if self.default_server_name == "Serenity": + s = re.search(r'(?<=code=)[a-zA-Z0-9\-_]*', dlg.ssoInfoCtrl.Value.strip()) + if s: + # skip state verification and go directly through the auth code processing + self.handleLogin(s.group) + else: + pass + # todo: throw error else: - pass - # todo: throw error - else: - self.handleServerRequest(json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip()))) + self.handleServerRequest(json.loads(base64.b64decode(dlg.ssoInfoCtrl.Value.strip()))) + except GenericSsoError as ex: + pyfalog.error(ex) + with wx.MessageDialog( + self.mainFrame, + str(ex), + _t("SSO Error"), + wx.OK | wx.ICON_ERROR + ) as dlg: + dlg.ShowModal() + except APIException as ex: + pyfalog.error(ex) + ESIExceptionHandler(ex) + pass def stopServer(self): From 27101732ca843624caccf31eb90209a35204c4f5 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 8 May 2022 13:48:38 -0400 Subject: [PATCH 06/10] Add a couple of bug fixes, and make the server option a checkbox --- .../pyfaEsiPreferences.py | 40 ++++++++++--------- gui/ssoLogin.py | 3 +- service/esi.py | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py index 0c0792e533..1aeb499dfb 100644 --- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -43,26 +43,22 @@ def populatePanel(self, panel): "due to 'Signature has expired' error"))) mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5) - rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize, - [_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS) - self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to" - " with information about the character login.")) - self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for" - " character login. Use this if having issues with the local server.")) - - self.rbMode.SetSelection(self.settings.get('loginMode')) - self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration" or True)) + self.ssoServer = wx.CheckBox(panel, wx.ID_ANY, _t("Auto-login (starts local server)"), wx.DefaultPosition, + wx.DefaultSize, + 0) + self.ssoServer.SetToolTip(wx.ToolTip(_t("This allows the EVE SSO to callback to your local pyfa instance and complete the authentication process without manual intervention."))) + mainSizer.Add(self.ssoServer, 0, wx.ALL | wx.EXPAND, 5) - rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) + rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) + self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration") or True) + self.ssoServer.SetValue(True if self.settings.get("loginMode") == 0 else False) mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0) esiSizer = wx.BoxSizer(wx.HORIZONTAL) - self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition,wx.DefaultSize, 0) + self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0) self.esiServer.Wrap(-1) @@ -76,10 +72,12 @@ def populatePanel(self, panel): esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10) - mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT , 10) + mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT, 10) self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange) self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange) + self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange) + mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) panel.SetSizer(mainSizer) @@ -88,19 +86,23 @@ def populatePanel(self, panel): def OnTimeoutChange(self, event): self.settings.set('timeout', event.GetEventObject().GetValue()) + event.Skip() def OnModeChange(self, event): - self.settings.set('loginMode', event.GetInt()) + self.settings.set('loginMode', 0 if self.ssoServer.GetValue() else 1) + event.Skip() def OnEnforceChange(self, event): self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue()) event.Skip() def OnServerChange(self, event): - source = self.chESIserver.GetString(self.chESIserver.GetSelection()) - esiService = Esi.getInstance() - esiService.init(config.supported_servers[source]) - self.settings.set("server", source) + pass + # source = self.chESIserver.GetString(self.chESIserver.GetSelection()) + # esiService = Esi.getInstance() + # # esiService.init(config.supported_servers[source]) + # self.settings.set("server", source) + # event.Skip() def getImage(self): return BitmapLoader.getBitmap("eve", "gui") diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py index cce7615ff1..1ca41067b6 100644 --- a/gui/ssoLogin.py +++ b/gui/ssoLogin.py @@ -88,6 +88,7 @@ def OnLogin(self, event): def OnDestroy(self, event): # Clean up by unbinding some events and stopping the server self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin) - self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy) + if self: + self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy) self.sEsi.stopServer() event.Skip() diff --git a/service/esi.py b/service/esi.py index 1bfa103b76..a465f09de8 100644 --- a/service/esi.py +++ b/service/esi.py @@ -108,7 +108,7 @@ def login(self): from gui.esiFittings import ESIExceptionHandler try: - if self.default_server_name == "Serenity": + if self.server_name == "Serenity": s = re.search(r'(?<=code=)[a-zA-Z0-9\-_]*', dlg.ssoInfoCtrl.Value.strip()) if s: # skip state verification and go directly through the auth code processing From ff14855808950baa110c5732f3972d56c2ae7a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A3=E6=B1=B0?= Date: Fri, 23 Feb 2024 02:47:27 +0800 Subject: [PATCH 07/10] Fix the esi problem for Serenity when setting --- .../pyfaEsiPreferences.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py index 1aeb499dfb..ba327a8d6f 100644 --- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -57,7 +57,7 @@ def populatePanel(self, panel): mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0) esiSizer = wx.BoxSizer(wx.HORIZONTAL) - + self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0) self.esiServer.Wrap(-1) @@ -78,8 +78,6 @@ def populatePanel(self, panel): self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange) self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange) - mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) - panel.SetSizer(mainSizer) panel.Layout() @@ -97,12 +95,13 @@ def OnEnforceChange(self, event): event.Skip() def OnServerChange(self, event): - pass - # source = self.chESIserver.GetString(self.chESIserver.GetSelection()) - # esiService = Esi.getInstance() - # # esiService.init(config.supported_servers[source]) - # self.settings.set("server", source) - # event.Skip() + # pass + source = self.chESIserver.GetString(self.chESIserver.GetSelection()) + esiService = Esi.getInstance() + # init servers + esiService.init(config.supported_servers[source]) + self.settings.set("server", source) + event.Skip() def getImage(self): return BitmapLoader.getBitmap("eve", "gui") From 6e3b7ff132c526d459d35a63524be2c8d0e9ead9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A3=E6=B1=B0?= Date: Fri, 23 Feb 2024 02:47:47 +0800 Subject: [PATCH 08/10] Fix the esi problem for Serenity when setting --- service/esiAccess.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/service/esiAccess.py b/service/esiAccess.py index aa5a507fbf..1181c176d6 100644 --- a/service/esiAccess.py +++ b/service/esiAccess.py @@ -1,6 +1,7 @@ # noinspection PyPackageRequirements from collections import namedtuple +import requests from logbook import Logger import uuid import time @@ -72,6 +73,8 @@ def __init__(self): self._session.headers.update(self._basicHeaders) self._session.proxies = NetworkSettings.getInstance().getProxySettingsInRequestsFormat() + self.mem_cached_session = {} + # Set up cached session. This is only used for SSO meta data for now, but can be expanded to actually handle # various ESI caching (using ETag, for example) in the future self.cached_session = CachedSession( @@ -88,11 +91,20 @@ def __init__(self): def init(self, server_base): self.server_base: config.ApiServer = server_base self.server_name = self.server_base.name - meta_call = self.cached_session.get("https://%s/.well-known/oauth-authorization-server" % self.server_base.sso) + try: + meta_call = self.cached_session.get("https://%s/.well-known/oauth-authorization-server" % self.server_base.sso) + except: + # The http data of expire_after in evepc.163.com is -1 + meta_call = requests.get("https://%s/.well-known/oauth-authorization-server" % self.server_base.sso) + meta_call.raise_for_status() self.server_meta = meta_call.json() - jwks_call = self.cached_session.get(self.server_meta["jwks_uri"]) + try: + jwks_call = self.cached_session.get(self.server_meta["jwks_uri"]) + except: + jwks_call = requests.get(self.server_meta["jwks_uri"]) + jwks_call.raise_for_status() self.jwks = jwks_call.json() From 94b1c8b029cf366f1917c5050e25627619576780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A3=E6=B1=B0?= Date: Fri, 23 Feb 2024 02:47:59 +0800 Subject: [PATCH 09/10] Fix the esi problem for Serenity when setting --- service/esi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/esi.py b/service/esi.py index a465f09de8..35d92a56fe 100644 --- a/service/esi.py +++ b/service/esi.py @@ -112,7 +112,7 @@ def login(self): s = re.search(r'(?<=code=)[a-zA-Z0-9\-_]*', dlg.ssoInfoCtrl.Value.strip()) if s: # skip state verification and go directly through the auth code processing - self.handleLogin(s.group) + self.handleLogin(s.group(0)) else: pass # todo: throw error From e314ecf887f7d6ec23329a8ba5e0c03b8939c632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A3=E6=B1=B0?= Date: Fri, 23 Feb 2024 02:48:54 +0800 Subject: [PATCH 10/10] Fix the esi problem for Serenity when setting --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index ea6a7ed7b6..f71e9ed38b 100644 --- a/config.py +++ b/config.py @@ -50,7 +50,7 @@ "Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True), # No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity # "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'), - "Serenity": ApiServer("Serenity", "login.evepc.163.com", "esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://esi.evepc.163.com/ui/oauth2-redirect.html', False) + "Serenity": ApiServer("Serenity", "login.evepc.163.com", "ali-esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://ali-esi.evepc.163.com/ui/oauth2-redirect.html', False) } SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff'