From 033647da61f5fd97f340fa255946140d502ed224 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sat, 17 Mar 2018 20:23:14 -0400 Subject: [PATCH 1/4] Move to a central login() method --- gui/characterEditor.py | 3 +-- gui/esiFittings.py | 3 +-- gui/globalEvents.py | 1 + service/esi.py | 7 +++++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 65dcb408f..c7a2fef8c 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -781,8 +781,7 @@ class APIView(wx.Panel): def addCharacter(self, event): sEsi = Esi.getInstance() - uri = sEsi.startServer() - webbrowser.open(uri) + sEsi.login() def getActiveCharacter(self): selection = self.charChoice.GetCurrentSelection() diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 2d8c5f9fa..31fdd5a71 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -344,8 +344,7 @@ class SsoCharacterMgmt(wx.Dialog): @staticmethod def addChar(event): sEsi = Esi.getInstance() - uri = sEsi.startServer() - webbrowser.open(uri) + sEsi.login() def delChar(self, event): item = self.lcCharacters.GetFirstSelected() diff --git a/gui/globalEvents.py b/gui/globalEvents.py index 1c1cc7e38..53784fcb6 100644 --- a/gui/globalEvents.py +++ b/gui/globalEvents.py @@ -5,5 +5,6 @@ FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent() CharListUpdated, CHAR_LIST_UPDATED = wx.lib.newevent.NewEvent() CharChanged, CHAR_CHANGED = wx.lib.newevent.NewEvent() +SsoLoggingIn, EVT_SSO_LOGGING_IN = wx.lib.newevent.NewEvent() SsoLogin, EVT_SSO_LOGIN = wx.lib.newevent.NewEvent() SsoLogout, EVT_SSO_LOGOUT = wx.lib.newevent.NewEvent() diff --git a/service/esi.py b/service/esi.py index f76f288d5..cd7879575 100644 --- a/service/esi.py +++ b/service/esi.py @@ -9,6 +9,7 @@ import base64 import json import os import config +import webbrowser import eos.db import datetime @@ -179,6 +180,12 @@ class Esi(object): if char.esi_client is not None: char.esi_client.security.update_token(tokenResponse) + def login(self): + # Switch off how we do things here depending on the mode of authentication + uri = self.startServer() + webbrowser.open(uri) + wx.PostEvent(self.mainFrame, GE.SsoLoggingIn()) + def stopServer(self): pyfalog.debug("Stopping Server") self.httpd.stop() From 199763bcca679e2bf68ff99ef5d6d397737be4fa Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sat, 17 Mar 2018 20:31:38 -0400 Subject: [PATCH 2/4] Separate the server login handler from the sso info handler --- service/esi.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/service/esi.py b/service/esi.py index cd7879575..3dbd6496c 100644 --- a/service/esi.py +++ b/service/esi.py @@ -207,24 +207,15 @@ class Esi(object): uri = esisecurity.get_auth_uri(state=self.state, redirect='http://localhost:{}'.format(port), pyfa_version=config.version) - self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleLogin,)) + self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleServerLogin,)) self.serverThread.name = "SsoCallbackServer" self.serverThread.daemon = True self.serverThread.start() return uri - def handleLogin(self, message): - if not message: - raise Exception("Could not parse out querystring parameters.") - - if message['state'][0] != self.state: - pyfalog.warn("OAUTH state mismatch") - raise Exception("OAUTH State Mismatch.") - - pyfalog.debug("Handling SSO login with: {0}", message) - - auth_response = json.loads(base64.b64decode(message['SSOInfo'][0])) + def handleLogin(self, ssoInfo): + auth_response = json.loads(base64.b64decode(ssoInfo)) # We need to preload the ESI Security object beforehand with the auth response so that we can use verify to # get character information @@ -246,5 +237,16 @@ class Esi(object): Esi.update_token(currentCharacter, auth_response) # this also sets the esi security token eos.db.save(currentCharacter) - wx.PostEvent(self.mainFrame, GE.SsoLogin(character = currentCharacter)) + wx.PostEvent(self.mainFrame, GE.SsoLogin(character=currentCharacter)) + def handleServerLogin(self, message): + if not message: + raise Exception("Could not parse out querystring parameters.") + + if message['state'][0] != self.state: + pyfalog.warn("OAUTH state mismatch") + raise Exception("OAUTH State Mismatch.") + + pyfalog.debug("Handling SSO login with: {0}", message) + + self.handleLogin(message['SSOInfo'][0]) From 49181dce2be2a75614d0f606410ac022268f05ef Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sat, 17 Mar 2018 21:08:11 -0400 Subject: [PATCH 3/4] Develop dialog to input SSO Info from pyfa.io, and handle the login. Still need to hook this up to a settings option --- gui/mainFrame.py | 10 ++++++++++ gui/ssoLogin.py | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 gui/ssoLogin.py diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2c8adaf90..ffa0364b1 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -57,6 +57,7 @@ from gui.setEditor import ImplantSetEditorDlg from gui.devTools import DevTools from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame +from gui.ssoLogin import SsoLogin from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard from gui.updateDialog import UpdateDialog @@ -237,6 +238,15 @@ class MainFrame(wx.Frame): self.sUpdate.CheckUpdate(self.ShowUpdateBox) self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) + self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin) + + def ShowSsoLogin(self, event): + dlg = SsoLogin(self) + if dlg.ShowModal() == wx.ID_OK: + sEsi = Esi.getInstance() + # todo: verify that this is a correct SSO Info block + sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip()) + def ShowUpdateBox(self, release, version): dlg = UpdateDialog(self, release, version) diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py new file mode 100644 index 000000000..2ff364752 --- /dev/null +++ b/gui/ssoLogin.py @@ -0,0 +1,25 @@ +import wx + +class SsoLogin(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240)) + + bSizer1 = wx.BoxSizer(wx.VERTICAL) + + text = wx.StaticText(self, wx.ID_ANY, "Copy and paste the block of text provided by pyfa.io, then click OK") + 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() + + bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | 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.OK | wx.CANCEL), 0, wx.EXPAND) + bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) + + self.SetSizer(bSizer1) + self.Center() From 53451dfaf680cd657abe501b2f690d0ba5d41136 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 18 Mar 2018 00:46:53 -0400 Subject: [PATCH 4/4] Implement a setting for manual vs server login method --- gui/builtinPreferenceViews/__init__.py | 1 + .../pyfaCrestPreferences.py | 148 ------------------ .../pyfaEsiPreferences.py | 137 ++++++++++++++++ gui/mainFrame.py | 14 +- gui/preferenceView.py | 2 +- service/esi.py | 42 +++-- service/settings.py | 22 +-- 7 files changed, 187 insertions(+), 179 deletions(-) delete mode 100644 gui/builtinPreferenceViews/pyfaCrestPreferences.py create mode 100644 gui/builtinPreferenceViews/pyfaEsiPreferences.py diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index ceec31971..32117a9ec 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -6,5 +6,6 @@ __all__ = [ "pyfaDatabasePreferences", "pyfaLoggingPreferences", "pyfaEnginePreferences", + "pyfaEsiPreferences", "pyfaStatViewPreferences"] diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py deleted file mode 100644 index 598019dab..000000000 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ /dev/null @@ -1,148 +0,0 @@ -# noinspection PyPackageRequirements -import wx - -from gui.preferenceView import PreferenceView -from gui.bitmap_loader import BitmapLoader - -import gui.mainFrame - -from service.settings import CRESTSettings - -# noinspection PyPackageRequirements -from wx.lib.intctrl import IntCtrl - -from service.esi import Esi - - -class PFCrestPref(PreferenceView): - title = "CREST" - - def populatePanel(self, panel): - - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = CRESTSettings.getInstance() - self.dirtySettings = False - dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) - self.stTitle.Wrap(-1) - self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - - mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - - self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - - self.stInfo = wx.StaticText(panel, wx.ID_ANY, - "Please see the pyfa wiki on GitHub for information regarding these options.", - wx.DefaultPosition, wx.DefaultSize, 0) - self.stInfo.Wrap(dlgWidth - 50) - mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - - rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, - ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) - self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, - ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) - - self.rbMode.SetSelection(self.settings.get('mode')) - self.rbServer.SetSelection(self.settings.get('server')) - - rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) - rbSizer.Add(self.rbServer, 1, wx.ALL, 5) - - self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) - self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) - - mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) - - timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) - - self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) - self.stTimout.Wrap(-1) - - timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) - timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) - self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) - - mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) - - detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) - detailsTitle.Wrap(-1) - detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - - mainSizer.Add(detailsTitle, 0, wx.ALL, 5) - mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, - wx.EXPAND, 5) - - fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) - fgAddrSizer.AddGrowableCol(1) - fgAddrSizer.SetFlexibleDirection(wx.BOTH) - fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - - self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) - self.stSetID.Wrap(-1) - fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, - wx.DefaultSize, 0) - - fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - - self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) - self.stSetSecret.Wrap(-1) - - fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, - wx.DefaultSize, 0) - - fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - - self.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) - self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) - - mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) - mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) - - self.ToggleProxySettings(self.settings.get('mode')) - - panel.SetSizer(mainSizer) - panel.Layout() - - def OnTimeoutChange(self, event): - self.settings.set('timeout', event.GetEventObject().GetValue()) - - def OnModeChange(self, event): - self.settings.set('mode', event.GetInt()) - self.ToggleProxySettings(self.settings.get('mode')) - - def OnServerChange(self, event): - self.settings.set('server', event.GetInt()) - - def OnBtnApply(self, event): - self.settings.set('clientID', self.inputClientID.GetValue().strip()) - self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sEsi = Esi.getInstance() - sEsi.delAllCharacters() - - def ToggleProxySettings(self, mode): - if mode: - self.stSetID.Enable() - self.inputClientID.Enable() - self.stSetSecret.Enable() - self.inputClientSecret.Enable() - else: - self.stSetID.Disable() - self.inputClientID.Disable() - self.stSetSecret.Disable() - self.inputClientSecret.Disable() - - def getImage(self): - return BitmapLoader.getBitmap("eve", "gui") - - -PFCrestPref.register() diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py new file mode 100644 index 000000000..029003a83 --- /dev/null +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -0,0 +1,137 @@ +# noinspection PyPackageRequirements +import wx + +from gui.preferenceView import PreferenceView +from gui.bitmap_loader import BitmapLoader + +import gui.mainFrame + +from service.settings import EsiSettings + +# noinspection PyPackageRequirements +from wx.lib.intctrl import IntCtrl + +from service.esi import Esi + + +class PFEsiPref(PreferenceView): + title = "EVE SSO" + + def populatePanel(self, panel): + + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.settings = EsiSettings.getInstance() + self.dirtySettings = False + dlgWidth = panel.GetParent().GetParent().ClientSize.width + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) + + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.stInfo = wx.StaticText(panel, wx.ID_ANY, + "Please see the pyfa wiki on GitHub for information regarding these options.", + wx.DefaultPosition, wx.DefaultSize, 0) + self.stInfo.Wrap(dlgWidth - 50) + mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + rbSizer = wx.BoxSizer(wx.HORIZONTAL) + self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize, + ['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS) + self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to with information about the character login.") + self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application to allow for character login. Use this if having issues with the local server.") + # self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, + # ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) + + self.rbMode.SetSelection(self.settings.get('loginMode')) + # self.rbServer.SetSelection(self.settings.get('server')) + + rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) + # rbSizer.Add(self.rbServer, 1, wx.ALL, 5) + + self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) + # self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) + + mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) + + timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) + + # self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stTimout.Wrap(-1) + # + # timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + # self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) + # timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) + # self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) + # + # mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) + + # detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) + # detailsTitle.Wrap(-1) + # detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + # + # mainSizer.Add(detailsTitle, 0, wx.ALL, 5) + # mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + # wx.EXPAND, 5) + + # fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + # fgAddrSizer.AddGrowableCol(1) + # fgAddrSizer.SetFlexibleDirection(wx.BOTH) + # fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + # + # self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stSetID.Wrap(-1) + # fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + # + # self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, + # wx.DefaultSize, 0) + # + # fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + # + # self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stSetSecret.Wrap(-1) + # + # fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + # + # self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, + # wx.DefaultSize, 0) + # + # fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + # + # self.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) + # self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) + # + # mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) + # mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) + + # self.ToggleProxySettings(self.settings.get('loginMode')) + + panel.SetSizer(mainSizer) + panel.Layout() + + def OnTimeoutChange(self, event): + self.settings.set('timeout', event.GetEventObject().GetValue()) + + def OnModeChange(self, event): + self.settings.set('loginMode', event.GetInt()) + + def OnServerChange(self, event): + self.settings.set('server', event.GetInt()) + + def OnBtnApply(self, event): + self.settings.set('clientID', self.inputClientID.GetValue().strip()) + self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) + sEsi = Esi.getInstance() + sEsi.delAllCharacters() + + def getImage(self): + return BitmapLoader.getBitmap("eve", "gui") + + +PFEsiPref.register() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index ffa0364b1..c9e5fa09c 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -83,7 +83,7 @@ import threading import webbrowser import wx.adv -from service.esi import Esi +from service.esi import Esi, LoginMethod from gui.esiFittings import EveFittings, ExportToEve, SsoCharacterMgmt disableOverrideEditor = False @@ -241,12 +241,12 @@ class MainFrame(wx.Frame): self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin) def ShowSsoLogin(self, event): - dlg = SsoLogin(self) - if dlg.ShowModal() == wx.ID_OK: - sEsi = Esi.getInstance() - # todo: verify that this is a correct SSO Info block - sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip()) - + if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL: + dlg = SsoLogin(self) + if dlg.ShowModal() == wx.ID_OK: + sEsi = Esi.getInstance() + # todo: verify that this is a correct SSO Info block + sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip()) def ShowUpdateBox(self, release, version): dlg = UpdateDialog(self, release, version) diff --git a/gui/preferenceView.py b/gui/preferenceView.py index 42ce5cd05..9ce2e676d 100644 --- a/gui/preferenceView.py +++ b/gui/preferenceView.py @@ -43,7 +43,7 @@ from gui.builtinPreferenceViews import ( # noqa: E402, F401 pyfaGeneralPreferences, pyfaNetworkPreferences, pyfaHTMLExportPreferences, - # pyfaCrestPreferences, + pyfaEsiPreferences, pyfaContextMenuPreferences, pyfaStatViewPreferences, pyfaUpdatePreferences, diff --git a/service/esi.py b/service/esi.py index 3dbd6496c..675a705ee 100644 --- a/service/esi.py +++ b/service/esi.py @@ -17,6 +17,7 @@ from eos.enum import Enum from eos.saveddata.ssocharacter import SsoCharacter import gui.globalEvents as GE from service.server import StoppableHTTPServer, AuthHandler +from service.settings import EsiSettings from .esi_security_proxy import EsiSecurityProxy from esipy import EsiClient, EsiApp @@ -39,6 +40,11 @@ class Servers(Enum): SISI = 1 +class LoginMethod(Enum): + SERVER = 0 + MANUAL = 1 + + class Esi(object): esiapp = None esi_v1 = None @@ -75,6 +81,8 @@ class Esi(object): def __init__(self): Esi.initEsiApp() + self.settings = EsiSettings.getInstance() + AFTER_TOKEN_REFRESH.add_receiver(self.tokenUpdate) # these will be set when needed @@ -119,7 +127,7 @@ class Esi(object): if char is not None and char.esi_client is None: char.esi_client = Esi.genEsiClient() Esi.update_token(char, Esi.get_sso_data(char)) # don't use update_token on security directly, se still need to apply the values here - print(repr(char)) + eos.db.commit() return char @@ -181,17 +189,33 @@ class Esi(object): char.esi_client.security.update_token(tokenResponse) def login(self): - # Switch off how we do things here depending on the mode of authentication - uri = self.startServer() + serverAddr = None + if self.settings.get('loginMode') == LoginMethod.SERVER: + serverAddr = self.startServer() + uri = self.getLoginURI(serverAddr) webbrowser.open(uri) - wx.PostEvent(self.mainFrame, GE.SsoLoggingIn()) + wx.PostEvent(self.mainFrame, GE.SsoLoggingIn(login_mode=self.settings.get('loginMode'))) def stopServer(self): pyfalog.debug("Stopping Server") self.httpd.stop() self.httpd = None - def startServer(self): + def getLoginURI(self, redirect=None): + self.state = str(uuid.uuid4()) + esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) + + args = { + 'state': self.state, + 'pyfa_version': config.version, + } + + if redirect is not None: + args['redirect'] = redirect + + return esisecurity.get_auth_uri(**args) + + def startServer(self): # todo: break this out into two functions: starting the server, and getting the URI pyfalog.debug("Starting server") # we need this to ensure that the previous get_request finishes, and then the socket will close @@ -199,20 +223,14 @@ class Esi(object): self.stopServer() time.sleep(1) - self.state = str(uuid.uuid4()) self.httpd = StoppableHTTPServer(('localhost', 0), AuthHandler) port = self.httpd.socket.getsockname()[1] - - esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) - - uri = esisecurity.get_auth_uri(state=self.state, redirect='http://localhost:{}'.format(port), pyfa_version=config.version) - self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleServerLogin,)) self.serverThread.name = "SsoCallbackServer" self.serverThread.daemon = True self.serverThread.start() - return uri + return 'http://localhost:{}'.format(port) def handleLogin(self, ssoInfo): auth_response = json.loads(base64.b64decode(ssoInfo)) diff --git a/service/settings.py b/service/settings.py index 7517255c5..ae5ce0e11 100644 --- a/service/settings.py +++ b/service/settings.py @@ -352,32 +352,32 @@ class UpdateSettings(object): self.serviceUpdateSettings[type] = value -class CRESTSettings(object): +class EsiSettings(object): _instance = None @classmethod def getInstance(cls): if cls._instance is None: - cls._instance = CRESTSettings() + cls._instance = EsiSettings() return cls._instance def __init__(self): - # mode - # 0 - Implicit authentication - # 1 - User-supplied client details - serviceCRESTDefaultSettings = {"mode": 0, "server": 0, "clientID": "", "clientSecret": "", "timeout": 60} + # LoginMode: + # 0 - Server Start Up + # 1 - User copy and paste data from website to pyfa + defaults = {"loginMode": 0, "clientID": "", "clientSecret": "", "timeout": 60} - self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings( - "pyfaServiceCRESTSettings", - serviceCRESTDefaultSettings + self.settings = SettingsProvider.getInstance().getSettings( + "pyfaServiceEsiSettings", + defaults ) def get(self, type): - return self.serviceCRESTSettings[type] + return self.settings[type] def set(self, type, value): - self.serviceCRESTSettings[type] = value + self.settings[type] = value class StatViewSettings(object):