Skip to content

Commit b8a58fc

Browse files
authored
Merge pull request #2590 from huangzheng2016/master
Update the SSO Login for Serenity and Singularity server's player
2 parents b04c521 + cc1fddd commit b8a58fc

File tree

12 files changed

+237
-123
lines changed

12 files changed

+237
-123
lines changed

config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from eos.const import FittingSlot
1010

1111
from cryptography.fernet import Fernet
12+
from collections import namedtuple
1213

1314
pyfalog = Logger(__name__)
1415

@@ -44,9 +45,16 @@
4445
version = None
4546
language = None
4647

47-
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
48+
ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback', 'supports_auto_login'])
49+
supported_servers = {
50+
"Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True),
51+
# No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity
52+
# "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'),
53+
"Serenity": ApiServer("Serenity", "login.evepc.163.com", "ali-esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://ali-esi.evepc.163.com/ui/oauth2-redirect.html', False)
54+
}
55+
56+
SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff'
4857
ESI_CACHE = 'esi_cache'
49-
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
5058

5159
LOGLEVEL_MAP = {
5260
"critical": CRITICAL,

eos/db/migrations/upgrade47.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
Migration 28
3+
4+
- adds baseItemID and mutaplasmidID to modules table
5+
"""
6+
import sqlalchemy
7+
8+
9+
10+
def upgrade(saveddata_engine):
11+
try:
12+
saveddata_engine.execute("SELECT server FROM ssoCharacter LIMIT 1")
13+
except sqlalchemy.exc.DatabaseError:
14+
saveddata_engine.execute("ALTER TABLE ssoCharacter ADD COLUMN server VARCHAR;")
15+
saveddata_engine.execute("UPDATE ssoCharacter SET server = 'Tranquility';")
16+
17+
18+
19+
# update all characters to TQ

eos/db/saveddata/character.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
Column("client", String, nullable=False),
4545
Column("characterID", Integer, nullable=False),
4646
Column("characterName", String, nullable=False),
47+
Column("server", String, nullable=False),
4748
Column("refreshToken", String, nullable=False),
4849
Column("accessToken", String, nullable=False),
4950
Column("accessTokenExpires", DateTime, nullable=False),

eos/db/saveddata/queries.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,12 @@ def getSsoCharacters(clientHash, eager=None):
493493

494494

495495
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
496-
def getSsoCharacter(lookfor, clientHash, eager=None):
496+
def getSsoCharacter(lookfor, clientHash, server=None, eager=None):
497497
filter = SsoCharacter.client == clientHash
498498

499+
if server is not None:
500+
filter = and_(filter, SsoCharacter.server == server)
501+
499502
if isinstance(lookfor, int):
500503
filter = and_(filter, SsoCharacter.ID == lookfor)
501504
elif isinstance(lookfor, str):

eos/saveddata/ssocharacter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525

2626

2727
class SsoCharacter:
28-
def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
28+
def __init__(self, charID, name, client, server, accessToken=None, refreshToken=None):
2929
self.characterID = charID
3030
self.characterName = name
3131
self.client = client
32+
self.server = server
3233
self.accessToken = accessToken
3334
self.refreshToken = refreshToken
3435
self.accessTokenExpires = None
@@ -37,6 +38,9 @@ def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
3738
def init(self):
3839
pass
3940

41+
@property
42+
def characterDisplay(self):
43+
return "{} [{}]".format(self.characterName, self.server)
4044
def is_token_expired(self):
4145
if self.accessTokenExpires is None:
4246
return True

gui/builtinPreferenceViews/pyfaEsiPreferences.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# noinspection PyPackageRequirements
22
import wx
33

4+
import config
45
import gui.mainFrame
56
from gui.bitmap_loader import BitmapLoader
67
from gui.preferenceView import PreferenceView
8+
from service.esi import Esi
79
from service.settings import EsiSettings
810

911
# noinspection PyPackageRequirements
@@ -41,38 +43,68 @@ def populatePanel(self, panel):
4143
"due to 'Signature has expired' error")))
4244
mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5)
4345

46+
self.ssoServer = wx.CheckBox(panel, wx.ID_ANY, _t("Auto-login (starts local server)"), wx.DefaultPosition,
47+
wx.DefaultSize,
48+
0)
49+
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.")))
50+
mainSizer.Add(self.ssoServer, 0, wx.ALL | wx.EXPAND, 5)
51+
4452
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
45-
self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize,
46-
[_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS)
47-
self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to"
48-
" with information about the character login."))
49-
self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for"
50-
" character login. Use this if having issues with the local server."))
5153

52-
self.rbMode.SetSelection(self.settings.get('loginMode'))
53-
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration" or True))
54+
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration") or True)
55+
self.ssoServer.SetValue(True if self.settings.get("loginMode") == 0 else False)
56+
57+
mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0)
58+
59+
esiSizer = wx.BoxSizer(wx.HORIZONTAL)
60+
61+
self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0)
62+
63+
self.esiServer.Wrap(-1)
64+
65+
esiSizer.Add(self.esiServer, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
66+
67+
self.esiServer.SetToolTip(wx.ToolTip(_t('The source you choose will be used on connection.')))
5468

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

57-
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
71+
self.chESIserver.SetStringSelection(self.settings.get("server"))
72+
73+
esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
74+
75+
mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT, 10)
76+
77+
self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange)
5878
self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange)
59-
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
79+
self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange)
6080

6181
panel.SetSizer(mainSizer)
82+
6283
panel.Layout()
6384

6485
def OnTimeoutChange(self, event):
6586
self.settings.set('timeout', event.GetEventObject().GetValue())
87+
event.Skip()
6688

6789
def OnModeChange(self, event):
68-
self.settings.set('loginMode', event.GetInt())
90+
self.settings.set('loginMode', 0 if self.ssoServer.GetValue() else 1)
91+
event.Skip()
6992

7093
def OnEnforceChange(self, event):
7194
self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue())
7295
event.Skip()
7396

97+
def OnServerChange(self, event):
98+
# pass
99+
source = self.chESIserver.GetString(self.chESIserver.GetSelection())
100+
esiService = Esi.getInstance()
101+
# init servers
102+
esiService.init(config.supported_servers[source])
103+
self.settings.set("server", source)
104+
event.Skip()
105+
74106
def getImage(self):
75107
return BitmapLoader.getBitmap("eve", "gui")
76108

77109

78-
PFEsiPref.register()
110+
PFEsiPref.register()

gui/characterEditor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ def charChanged(self, event):
864864
noneID = self.charChoice.Append(_t("None"), None)
865865

866866
for char in ssoChars:
867-
currId = self.charChoice.Append(char.characterName, char.ID)
867+
currId = self.charChoice.Append(char.characterDisplay, char.ID)
868868

869869
if sso is not None and char.ID == sso.ID:
870870
self.charChoice.SetSelection(currId)

gui/esiFittings.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def updateCharList(self):
9696

9797
self.charChoice.Clear()
9898
for char in chars:
99-
self.charChoice.Append(char.characterName, char.ID)
99+
self.charChoice.Append(char.characterDisplay, char.ID)
100100
if len(chars) > 0:
101101
self.charChoice.SetSelection(0)
102102

@@ -227,21 +227,6 @@ def deleteAllFittings(self, event):
227227
self.fitView.update([])
228228

229229

230-
class ESIServerExceptionHandler:
231-
def __init__(self, parentWindow, ex):
232-
pyfalog.error(ex)
233-
with wx.MessageDialog(
234-
parentWindow,
235-
_t("There was an issue starting up the localized server, try setting "
236-
"Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> "
237-
"Login Authentication Method. If this doesn't fix the problem please file an "
238-
"issue on Github."),
239-
_t("Add Character Error"),
240-
wx.OK | wx.ICON_ERROR
241-
) as dlg:
242-
dlg.ShowModal()
243-
244-
245230
class ESIExceptionHandler:
246231
# todo: make this a generate excetpion handler for all calls
247232
def __init__(self, ex):
@@ -348,7 +333,7 @@ def updateCharList(self):
348333

349334
self.charChoice.Clear()
350335
for char in chars:
351-
self.charChoice.Append(char.characterName, char.ID)
336+
self.charChoice.Append(char.characterDisplay, char.ID)
352337

353338
if len(chars) > 0:
354339
self.charChoice.SetSelection(0)
@@ -434,6 +419,7 @@ def __init__(self, parent):
434419

435420
self.lcCharacters.InsertColumn(0, heading=_t('Character'))
436421
self.lcCharacters.InsertColumn(1, heading=_t('Character ID'))
422+
self.lcCharacters.InsertColumn(2, heading=_t('Server'))
437423

438424
self.popCharList()
439425

@@ -496,18 +482,18 @@ def popCharList(self):
496482
self.lcCharacters.InsertItem(index, char.characterName)
497483
self.lcCharacters.SetItem(index, 1, str(char.characterID))
498484
self.lcCharacters.SetItemData(index, char.ID)
485+
self.lcCharacters.SetItem(index, 2, char.server or "<unknown>")
499486

500487
self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE)
501488
self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE)
489+
self.lcCharacters.SetColumnWidth(2, wx.LIST_AUTOSIZE)
502490

503491
def addChar(self, event):
504492
try:
505493
sEsi = Esi.getInstance()
506494
sEsi.login()
507495
except (KeyboardInterrupt, SystemExit):
508496
raise
509-
except Exception as ex:
510-
ESIServerExceptionHandler(self, ex)
511497

512498
def delChar(self, event):
513499
item = self.lcCharacters.GetFirstSelected()

gui/ssoLogin.py

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,48 @@
22
import gui.mainFrame
33
import webbrowser
44
import gui.globalEvents as GE
5+
import config
6+
import time
7+
8+
from service.settings import EsiSettings
59

610
_t = wx.GetTranslation
711

812

913
class SsoLogin(wx.Dialog):
1014

11-
def __init__(self):
12-
mainFrame = gui.mainFrame.MainFrame.getInstance()
13-
15+
def __init__(self, server: config.ApiServer, start_local_server=True):
16+
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
17+
from service.esi import Esi
1418
super().__init__(
15-
mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
19+
self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
1620
size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
1721

1822
bSizer1 = wx.BoxSizer(wx.VERTICAL)
1923

20-
text = wx.StaticText(self, wx.ID_ANY, _t("Copy and paste the block of text provided by pyfa.io"))
21-
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
24+
if start_local_server:
25+
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
26+
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
27+
bSizer1.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.EXPAND, 15)
28+
text = wx.StaticText(self, wx.ID_ANY, _t("If auto-login fails, copy and paste the token provided by pyfa.io"))
29+
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
30+
elif server.name == "Serenity":
31+
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the url when your authorization is completed"))
32+
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
33+
34+
else:
35+
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the token provided by pyfa.io"))
36+
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
2237

2338
self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE)
2439
self.ssoInfoCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
2540
self.ssoInfoCtrl.Layout()
41+
self.ssoInfoCtrl.Bind(wx.EVT_TEXT, self.OnTextEnter)
2642

2743
bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10)
2844

45+
self.Esisettings = EsiSettings.getInstance()
46+
2947
bSizer3 = wx.BoxSizer(wx.VERTICAL)
3048
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
3149

@@ -34,51 +52,43 @@ def __init__(self):
3452

3553
self.SetSizer(bSizer1)
3654
self.Center()
37-
38-
from service.esi import Esi
39-
4055
self.sEsi = Esi.getInstance()
41-
uri = self.sEsi.get_login_uri(None)
42-
webbrowser.open(uri)
4356

57+
serverAddr = self.sEsi.startServer(0) if start_local_server else None
58+
uri = self.sEsi.get_login_uri(serverAddr)
4459

45-
class SsoLoginServer(wx.Dialog):
60+
if server.name == "Serenity":
61+
webbrowser.open(config.SSO_LOGOFF_SERENITY)
62+
time.sleep(1)
4663

47-
def __init__(self, port):
48-
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
49-
super().__init__(self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
64+
self.okBtn = self.FindWindow(wx.ID_OK)
65+
self.okBtn.Enable(False)
66+
# Ensure we clean up once they hit the "OK" button
67+
self.okBtn.Bind(wx.EVT_BUTTON, self.OnDestroy)
5068

51-
from service.esi import Esi
52-
53-
self.sEsi = Esi.getInstance()
54-
serverAddr = self.sEsi.startServer(port)
55-
56-
uri = self.sEsi.get_login_uri(serverAddr)
69+
webbrowser.open(uri)
5770

58-
bSizer1 = wx.BoxSizer(wx.VERTICAL)
5971
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
72+
# Ensure we clean up if ESC is pressed
6073
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
6174

62-
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
63-
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
64-
65-
bSizer3 = wx.BoxSizer(wx.VERTICAL)
66-
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
67-
68-
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.CANCEL), 0, wx.EXPAND)
69-
bSizer1.Add(bSizer3, 0, wx.BOTTOM | wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
70-
71-
self.SetSizer(bSizer1)
72-
self.Fit()
73-
self.Center()
74-
75-
webbrowser.open(uri)
75+
def OnTextEnter(self, event):
76+
t = event.String.strip()
77+
if t == "":
78+
self.okBtn.Enable(False)
79+
else:
80+
self.okBtn.Enable(True)
81+
event.Skip()
7682

7783
def OnLogin(self, event):
78-
self.EndModal(wx.ID_OK)
84+
# 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
85+
self.EndModal(wx.ID_CANCEL)
7986
event.Skip()
8087

8188
def OnDestroy(self, event):
89+
# Clean up by unbinding some events and stopping the server
8290
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin)
91+
if self:
92+
self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy)
8393
self.sEsi.stopServer()
8494
event.Skip()

0 commit comments

Comments
 (0)