Implemented shared sessions between webclient and website - logging into either will also log in the player to the other. This is addresses the first point of #613.
This commit is contained in:
parent
eebd41f46d
commit
f04c82fa17
7 changed files with 111 additions and 81 deletions
|
|
@ -53,20 +53,27 @@ class WebSocketClient(Protocol, Session):
|
||||||
self.transport.validationMade = self.validationMade
|
self.transport.validationMade = self.validationMade
|
||||||
client_address = self.transport.client
|
client_address = self.transport.client
|
||||||
client_address = client_address[0] if client_address else None
|
client_address = client_address[0] if client_address else None
|
||||||
print ("connectionMade: webclient address", client_address, self.transport.client, self.transport.client.__dict__, self.transport.__dict__)
|
|
||||||
|
|
||||||
self.init_session("websocket", client_address, self.factory.sessionhandler)
|
self.init_session("websocket", client_address, self.factory.sessionhandler)
|
||||||
# watch for dead links
|
|
||||||
self.transport.setTcpKeepAlive(1)
|
|
||||||
self.sessionhandler.connect(self)
|
|
||||||
|
|
||||||
def validationMade(self):
|
def validationMade(self):
|
||||||
"""
|
"""
|
||||||
This is called from the (modified) txws websocket library when
|
This is called from the (modified) txws websocket library when
|
||||||
the ws handshake and validation has completed fully.
|
the ws handshake and validation has completed fully.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#print "validationMade:", self.transport.location.split("?", 1)[1]
|
|
||||||
pass
|
self.csessid = self.transport.location.split("?", 1)[1]
|
||||||
|
csession = _CLIENT_SESSIONS(session_key=self.csessid)
|
||||||
|
uid = csession and csession.get("logged_in", False)
|
||||||
|
if uid:
|
||||||
|
# the client session is already logged in.
|
||||||
|
self.uid = uid
|
||||||
|
self.logged_in = True
|
||||||
|
|
||||||
|
# watch for dead links
|
||||||
|
self.transport.setTcpKeepAlive(1)
|
||||||
|
# actually do the connection
|
||||||
|
self.sessionhandler.connect(self)
|
||||||
|
|
||||||
def disconnect(self, reason=None):
|
def disconnect(self, reason=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -139,19 +146,6 @@ class WebSocketClient(Protocol, Session):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if "csessid" in kwargs and self.csessid is None:
|
|
||||||
# only allow to change csessid on the very first connect
|
|
||||||
# - this is a safety measure to avoid a clself.transport.client.__dict__, ient to manually
|
|
||||||
# change its csessid later.
|
|
||||||
self.csessid = kwargs.pop("csessid")
|
|
||||||
csession = _CLIENT_SESSIONS(session_key=self.csessid)
|
|
||||||
uid = csession and csession.get("logged_in", False)
|
|
||||||
if uid:
|
|
||||||
# the browser session is already logged in.
|
|
||||||
self.uid = uid
|
|
||||||
self.logged_in = True
|
|
||||||
return
|
|
||||||
|
|
||||||
if "websocket_close" in kwargs:
|
if "websocket_close" in kwargs:
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ from evennia.utils import utils
|
||||||
from evennia.utils.text2html import parse_html
|
from evennia.utils.text2html import parse_html
|
||||||
from evennia.server import session
|
from evennia.server import session
|
||||||
|
|
||||||
|
_CLIENT_SESSIONS = utils.mod_import(settings.SESSION_ENGINE).SessionStore
|
||||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||||
_SERVERNAME = settings.SERVERNAME
|
_SERVERNAME = settings.SERVERNAME
|
||||||
_KEEPALIVE = 30 # how often to check keepalive
|
_KEEPALIVE = 30 # how often to check keepalive
|
||||||
|
|
@ -149,7 +150,7 @@ class WebClient(resource.Resource):
|
||||||
request (Request): Incoming request.
|
request (Request): Incoming request.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
csessid = request.args.get('csessid')
|
csessid = request.args.get('csessid')[0]
|
||||||
|
|
||||||
remote_addr = request.getClientIP()
|
remote_addr = request.getClientIP()
|
||||||
host_string = "%s (%s:%s)" % (_SERVERNAME, request.getRequestHostname(), request.getHost().port)
|
host_string = "%s (%s:%s)" % (_SERVERNAME, request.getRequestHostname(), request.getHost().port)
|
||||||
|
|
@ -157,7 +158,15 @@ class WebClient(resource.Resource):
|
||||||
sess = WebClientSession()
|
sess = WebClientSession()
|
||||||
sess.client = self
|
sess.client = self
|
||||||
sess.init_session("ajax/comet", remote_addr, self.sessionhandler)
|
sess.init_session("ajax/comet", remote_addr, self.sessionhandler)
|
||||||
|
|
||||||
sess.csessid = csessid
|
sess.csessid = csessid
|
||||||
|
csession = _CLIENT_SESSIONS(session_key=sess.csessid)
|
||||||
|
uid = csession and csession.get("logged_in", False)
|
||||||
|
if uid:
|
||||||
|
# the client session is already logged in
|
||||||
|
sess.uid = uid
|
||||||
|
sess.logged_in = True
|
||||||
|
|
||||||
sess.sessionhandler.connect(sess)
|
sess.sessionhandler.connect(sess)
|
||||||
|
|
||||||
self.last_alive[csessid] = (time(), False)
|
self.last_alive[csessid] = (time(), False)
|
||||||
|
|
|
||||||
|
|
@ -227,10 +227,10 @@ class ServerSession(Session):
|
||||||
# An existing client sessid is registered, thus a matching
|
# An existing client sessid is registered, thus a matching
|
||||||
# Client Session must also exist. Update it so the website
|
# Client Session must also exist. Update it so the website
|
||||||
# can also see we are logged in.
|
# can also see we are logged in.
|
||||||
csession = ClientSessionStore(session_key=self.browserid)
|
csession = ClientSessionStore(session_key=self.csessid)
|
||||||
csession["logged_in"] = player.id
|
if not csession.get("logged_in"):
|
||||||
csession.save()
|
csession["logged_in"] = player.id
|
||||||
print ("serversession.login:", csession.session_key)
|
csession.save()
|
||||||
|
|
||||||
# Update account's last login time.
|
# Update account's last login time.
|
||||||
self.player.last_login = timezone.now()
|
self.player.last_login = timezone.now()
|
||||||
|
|
|
||||||
|
|
@ -252,14 +252,24 @@ class ServerSessionHandler(SessionHandler):
|
||||||
sess = _ServerSession()
|
sess = _ServerSession()
|
||||||
sess.sessionhandler = self
|
sess.sessionhandler = self
|
||||||
sess.load_sync_data(portalsessiondata)
|
sess.load_sync_data(portalsessiondata)
|
||||||
if sess.logged_in and sess.uid:
|
|
||||||
# this can happen in the case of auto-authenticating
|
|
||||||
# protocols like SSH
|
|
||||||
sess.player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
|
||||||
sess.at_sync()
|
sess.at_sync()
|
||||||
# validate all scripts
|
# validate all scripts
|
||||||
_ScriptDB.objects.validate()
|
_ScriptDB.objects.validate()
|
||||||
self[sess.sessid] = sess
|
self[sess.sessid] = sess
|
||||||
|
|
||||||
|
if sess.logged_in and sess.uid:
|
||||||
|
# Session is already logged in. This can happen in the
|
||||||
|
# case of auto-authenticating protocols like SSH or
|
||||||
|
# webclient's session sharing
|
||||||
|
player = _PlayerDB.objects.get_player_from_uid(sess.uid)
|
||||||
|
if player:
|
||||||
|
self.login(sess, player, force=True)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sess.logged_in = False
|
||||||
|
sess.uid = None
|
||||||
|
|
||||||
|
# show the first login command
|
||||||
self.data_in(sess, text=[[CMD_LOGINSTART],{}])
|
self.data_in(sess, text=[[CMD_LOGINSTART],{}])
|
||||||
|
|
||||||
def portal_session_sync(self, portalsessiondata):
|
def portal_session_sync(self, portalsessiondata):
|
||||||
|
|
@ -355,7 +365,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION,
|
self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION,
|
||||||
operation=SSHUTD)
|
operation=SSHUTD)
|
||||||
|
|
||||||
def login(self, session, player, testmode=False):
|
def login(self, session, player, force=False, testmode=False):
|
||||||
"""
|
"""
|
||||||
Log in the previously unloggedin session and the player we by
|
Log in the previously unloggedin session and the player we by
|
||||||
now should know is connected to it. After this point we assume
|
now should know is connected to it. After this point we assume
|
||||||
|
|
@ -364,12 +374,14 @@ class ServerSessionHandler(SessionHandler):
|
||||||
Args:
|
Args:
|
||||||
session (Session): The Session to authenticate.
|
session (Session): The Session to authenticate.
|
||||||
player (Player): The Player identified as associated with this Session.
|
player (Player): The Player identified as associated with this Session.
|
||||||
|
force (bool): Login also if the session thinks it's already logged in
|
||||||
|
(this can happen for auto-authenticating protocols)
|
||||||
testmode (bool, optional): This is used by unittesting for
|
testmode (bool, optional): This is used by unittesting for
|
||||||
faking login without any AMP being actually active.
|
faking login without any AMP being actually active.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if session.logged_in:
|
if session.logged_in and not force:
|
||||||
# don't log in a session that is already logged in.
|
# don't log in a session that is already logged in.
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ An "emitter" object must have a function
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
this.emitter = opts.emitter || new DefaultEmitter();
|
this.emitter = opts.emitter || new DefaultEmitter();
|
||||||
|
|
||||||
if (opts.ckonnection) {
|
if (opts.connection) {
|
||||||
this.connection = opts.connection;
|
this.connection = opts.connection;
|
||||||
}
|
}
|
||||||
else if (window.WebSocket && wsactive) {
|
else if (window.WebSocket && wsactive) {
|
||||||
|
|
@ -224,6 +224,7 @@ An "emitter" object must have a function
|
||||||
// No-op if a connection is already open.
|
// No-op if a connection is already open.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Important - we pass csessid tacked on the url
|
||||||
websocket = new WebSocket(wsurl + '?' + csessid);
|
websocket = new WebSocket(wsurl + '?' + csessid);
|
||||||
|
|
||||||
// Handle Websocket open event
|
// Handle Websocket open event
|
||||||
|
|
@ -231,7 +232,6 @@ An "emitter" object must have a function
|
||||||
open = true;
|
open = true;
|
||||||
ever_open = true;
|
ever_open = true;
|
||||||
Evennia.emit('connection_open', ["websocket"], event);
|
Evennia.emit('connection_open', ["websocket"], event);
|
||||||
Evennia.msg('csessid', [csessid], {});
|
|
||||||
};
|
};
|
||||||
// Handle Websocket close event
|
// Handle Websocket close event
|
||||||
websocket.onclose = function (event) {
|
websocket.onclose = function (event) {
|
||||||
|
|
|
||||||
|
|
@ -8,38 +8,45 @@ from __future__ import print_function
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
|
|
||||||
from evennia.server.sessionhandler import SESSION_HANDLER
|
|
||||||
from evennia.players.models import PlayerDB
|
from evennia.players.models import PlayerDB
|
||||||
|
from evennia.utils import logger
|
||||||
|
|
||||||
|
|
||||||
|
def _shared_login(request):
|
||||||
|
"""
|
||||||
|
Handle the shared login between website and webclient.
|
||||||
|
|
||||||
|
"""
|
||||||
|
csession = request.session
|
||||||
|
player = request.user
|
||||||
|
sesslogin = csession.get("logged_in", None)
|
||||||
|
|
||||||
|
# check if user has authenticated to website
|
||||||
|
if csession.session_key is None:
|
||||||
|
# this is necessary to build the sessid key
|
||||||
|
csession.save()
|
||||||
|
elif player.is_authenticated():
|
||||||
|
if not sesslogin:
|
||||||
|
# User has already authenticated to website
|
||||||
|
csession["logged_in"] = player.id
|
||||||
|
elif sesslogin:
|
||||||
|
# The webclient has previously registered a login to this browser_session
|
||||||
|
player = PlayerDB.objects.get(id=sesslogin)
|
||||||
|
try:
|
||||||
|
login(request, player)
|
||||||
|
except AttributeError:
|
||||||
|
logger.log_trace()
|
||||||
|
|
||||||
|
|
||||||
def webclient(request):
|
def webclient(request):
|
||||||
"""
|
"""
|
||||||
Webclient page template loading.
|
Webclient page template loading.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
print ("webclient session:", request.session.session_key, request.user, request.user.is_authenticated())
|
# handle webclient-website shared login
|
||||||
|
_shared_login(request)
|
||||||
|
|
||||||
csession = request.session
|
# make sure to store the browser session's hash so the webclient can get to it!
|
||||||
csessid = request.session.session_key
|
|
||||||
player = request.user
|
|
||||||
# check if user has authenticated to website
|
|
||||||
if player.is_authenticated():
|
|
||||||
print ("webclient: player auth, trying to connect sessions")
|
|
||||||
# Try to login all the player's webclient sessions - only
|
|
||||||
# unloggedin ones will actually be logged in.
|
|
||||||
for session in SESSION_HANDLER.sessions_from_csessid(csessid):
|
|
||||||
print ("session to connect:", session)
|
|
||||||
if session.protocol_key in ("websocket", "ajax/comet"):
|
|
||||||
SESSION_HANDLER.login(session, player)
|
|
||||||
csession["logged_in"] = player.id
|
|
||||||
elif csession.get("logged_in"):
|
|
||||||
# The webclient has previously registered a login to this browser_session
|
|
||||||
print ("webclient: browser_session logged in, trying to login")
|
|
||||||
player = PlayerDB.objects.get(csession.get("logged_in"))
|
|
||||||
login(player, request)
|
|
||||||
else:
|
|
||||||
csession["logged_in"] = False
|
|
||||||
|
|
||||||
# make sure to store the browser session's hash so the webclient can get to it
|
|
||||||
pagevars = {'browser_sessid': request.session.session_key}
|
pagevars = {'browser_sessid': request.session.session_key}
|
||||||
|
|
||||||
return render(request, 'webclient.html', pagevars)
|
return render(request, 'webclient.html', pagevars)
|
||||||
|
|
|
||||||
|
|
@ -13,43 +13,38 @@ from django.shortcuts import render
|
||||||
from evennia import SESSION_HANDLER
|
from evennia import SESSION_HANDLER
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.players.models import PlayerDB
|
from evennia.players.models import PlayerDB
|
||||||
|
from evennia.utils import logger
|
||||||
|
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
|
|
||||||
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
|
||||||
|
|
||||||
def page_index(request):
|
def _shared_login(request):
|
||||||
"""
|
"""
|
||||||
Main root page.
|
Handle the shared login between website and webclient.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# handle webclient-website shared login
|
|
||||||
|
|
||||||
csession = request.session
|
csession = request.session
|
||||||
csessid = request.session.session_key
|
|
||||||
player = request.user
|
player = request.user
|
||||||
# check if user has authenticated to website
|
sesslogin = csession.get("logged_in", None)
|
||||||
if player.is_authenticated():
|
|
||||||
# Try to login all the player's webclient sessions - only
|
if csession.session_key is None:
|
||||||
# unloggedin ones will actually be logged in.
|
# this is necessary to build the sessid key
|
||||||
print "website: player auth, trying to connect sessions"
|
csession.save()
|
||||||
for session in SESSION_HANDLER.sessions_from_csessid(csessid):
|
elif player.is_authenticated():
|
||||||
print "session to connect:", session
|
if not sesslogin:
|
||||||
if session.protocol_key in ("websocket", "ajax/comet"):
|
csession["logged_in"] = player.id
|
||||||
SESSION_HANDLER.login(session, player)
|
elif sesslogin:
|
||||||
session.csessid = csession.session_key
|
|
||||||
csession["logged_in"] = player.id
|
|
||||||
elif csession.get("logged_in"):
|
|
||||||
# The webclient has previously registered a login to this csession
|
# The webclient has previously registered a login to this csession
|
||||||
print "website: csession logged in, trying to login"
|
player = PlayerDB.objects.get(id=sesslogin)
|
||||||
player = PlayerDB.objects.get(id=csession.get("logged_in"))
|
try:
|
||||||
login(request, player)
|
login(request, player)
|
||||||
else:
|
except AttributeError:
|
||||||
csession["logged_in"] = None
|
logger.log_trace()
|
||||||
|
|
||||||
print ("website session:", request.session.session_key, request.user, request.user.is_authenticated())
|
|
||||||
|
|
||||||
|
def _gamestats():
|
||||||
# Some misc. configurable stuff.
|
# Some misc. configurable stuff.
|
||||||
# TODO: Move this to either SQL or settings.py based configuration.
|
# TODO: Move this to either SQL or settings.py based configuration.
|
||||||
fpage_player_limit = 4
|
fpage_player_limit = 4
|
||||||
|
|
@ -81,6 +76,19 @@ def page_index(request):
|
||||||
"num_characters": nchars or "no",
|
"num_characters": nchars or "no",
|
||||||
"num_others": nothers or "no"
|
"num_others": nothers or "no"
|
||||||
}
|
}
|
||||||
|
return pagevars
|
||||||
|
|
||||||
|
|
||||||
|
def page_index(request):
|
||||||
|
"""
|
||||||
|
Main root page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# handle webclient-website shared login
|
||||||
|
_shared_login(request)
|
||||||
|
|
||||||
|
# get game db stats
|
||||||
|
pagevars = _gamestats()
|
||||||
|
|
||||||
return render(request, 'index.html', pagevars)
|
return render(request, 'index.html', pagevars)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue