Skip to content

Commit

Permalink
Merge pull request #2590 from huangzheng2016/master
Browse files Browse the repository at this point in the history
Update the SSO Login for Serenity and Singularity server's player
  • Loading branch information
DarkFenX authored Feb 26, 2024
2 parents b04c521 + cc1fddd commit b8a58fc
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 123 deletions.
12 changes: 10 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from eos.const import FittingSlot

from cryptography.fernet import Fernet
from collections import namedtuple

pyfalog = Logger(__name__)

Expand Down Expand Up @@ -44,9 +45,16 @@
version = None
language = None

API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
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', 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", "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'
ESI_CACHE = 'esi_cache'
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'

LOGLEVEL_MAP = {
"critical": CRITICAL,
Expand Down
19 changes: 19 additions & 0 deletions eos/db/migrations/upgrade47.py
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions eos/db/saveddata/character.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
5 changes: 4 additions & 1 deletion eos/db/saveddata/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
6 changes: 5 additions & 1 deletion eos/saveddata/ssocharacter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
58 changes: 45 additions & 13 deletions gui/builtinPreferenceViews/pyfaEsiPreferences.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -41,38 +43,68 @@ def populatePanel(self, panel):
"due to 'Signature has expired' error")))
mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5)

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 = 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.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.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.')))

rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
self.chESIserver = wx.Choice(panel, choices=list(self.settings.keys()))

self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
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)
self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange)
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange)

panel.SetSizer(mainSizer)

panel.Layout()

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):
# 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")


PFEsiPref.register()
PFEsiPref.register()
2 changes: 1 addition & 1 deletion gui/characterEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,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)
Expand Down
24 changes: 5 additions & 19 deletions gui/esiFittings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -348,7 +333,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)
Expand Down Expand Up @@ -434,6 +419,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()

Expand Down Expand Up @@ -496,18 +482,18 @@ 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 "<unknown>")

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:
sEsi = Esi.getInstance()
sEsi.login()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
ESIServerExceptionHandler(self, ex)

def delChar(self, event):
item = self.lcCharacters.GetFirstSelected()
Expand Down
84 changes: 47 additions & 37 deletions gui/ssoLogin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,48 @@
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)

self.Esisettings = EsiSettings.getInstance()

bSizer3 = wx.BoxSizer(wx.VERTICAL)
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)

Expand All @@ -34,51 +52,43 @@ 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)

serverAddr = self.sEsi.startServer(0) if start_local_server else None
uri = self.sEsi.get_login_uri(serverAddr)

class SsoLoginServer(wx.Dialog):
if server.name == "Serenity":
webbrowser.open(config.SSO_LOGOFF_SERENITY)
time.sleep(1)

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)
self.okBtn = self.FindWindow(wx.ID_OK)
self.okBtn.Enable(False)
# Ensure we clean up once they hit the "OK" button
self.okBtn.Bind(wx.EVT_BUTTON, self.OnDestroy)

from service.esi import Esi

self.sEsi = Esi.getInstance()
serverAddr = self.sEsi.startServer(port)

uri = self.sEsi.get_login_uri(serverAddr)
webbrowser.open(uri)

bSizer1 = wx.BoxSizer(wx.VERTICAL)
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)

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)
# 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)
if self:
self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy)
self.sEsi.stopServer()
event.Skip()
Loading

0 comments on commit b8a58fc

Please sign in to comment.