First working version of the shared web login.

This commit is contained in:
Griatch 2016-05-30 21:36:38 +02:00
parent 81170b69d0
commit a31441b3ce
9 changed files with 130 additions and 8 deletions

View file

@ -6,6 +6,7 @@ import time
from collections import defaultdict
from random import getrandbits
from django.conf import settings
from django.contrib.auth import authenticate
from evennia.players.models import PlayerDB
from evennia.objects.models import ObjectDB
from evennia.server.models import ServerConfig
@ -148,17 +149,15 @@ def create_normal_player(session, name, password):
return None
# Match account name and check password
player = PlayerDB.objects.get_player_from_name(name)
pswd = None
if player:
pswd = player.check_password(password)
player = authenticate(username=name, password=password)
if not (player and pswd):
if not player:
# No playername or password match
session.msg("Incorrect login information given.")
# this just updates the throttle
_throttle(session)
# calls player hook for a failed login if possible.
player = PlayerDB.objects.get_player_from_name(name)
if player:
player.at_failed_login(session)
return None

View file

@ -20,11 +20,16 @@ settings.INPUT_FUNC_MODULES.
"""
from future.utils import viewkeys
import importlib
from django.conf import settings
from evennia.commands.cmdhandler import cmdhandler
from evennia.players.models import PlayerDB
from evennia.utils.logger import log_err
from evennia.utils.utils import to_str, to_unicode
# django browser sessions
BrowserSessionStore = importlib.import_module(settings.SESSION_ENGINE).SessionStore
# always let "idle" work since we use this in the webclient
_IDLE_COMMAND = settings.IDLE_COMMAND
@ -35,6 +40,7 @@ _NA = lambda o: "N/A"
_ERROR_INPUT = "Inputfunc {name}({session}): Wrong/unrecognized input: {inp}"
# All global functions are inputfuncs available to process inputs
def text(session, *args, **kwargs):
@ -99,6 +105,35 @@ def default(session, cmdname, *args, **kwargs):
log_err(err)
def browser_sessid(session, *args, **kwargs):
"""
This is a utility function for the webclient (only) to communicate its
current browser session hash. This is important in order to link
the browser session to the evennia session. Only the very first
storage request will be accepted, the following ones will be ignored.
Args:
browserid (str): Browser session hash
"""
if not session.browserid:
print "stored browserid:", session, args[0]
session.browserid = args[0]
if not session.logged_in:
# automatic log in if the django browser session already authenticated.
browsersession = BrowserSessionStore(session_key=args[0])
uid = browsersession.get("logged_in", None)
if uid:
try:
player = PlayerDB.objects.get(pk=uid)
except Exception:
return
session.sessionhandler.login(session, player)
def client_options(session, *args, **kwargs):
"""
This allows the client an OOB way to inform us about its name and capabilities.

View file

@ -38,6 +38,7 @@ from evennia.utils.text2html import parse_html
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
class WebSocketClient(Protocol, Session):
"""
Implements the server-side of the Websocket connection.

View file

@ -10,6 +10,7 @@ from builtins import object
import re
import weakref
import importlib
from time import time
from django.utils import timezone
from django.conf import settings
@ -19,6 +20,8 @@ from evennia.utils.utils import make_iter, lazy_property
from evennia.commands.cmdsethandler import CmdSetHandler
from evennia.server.session import Session
BrowserSessionStore = importlib.import_module(settings.SESSION_ENGINE).SessionStore
_GA = object.__getattribute__
_SA = object.__setattr__
_ObjectDB = None
@ -160,6 +163,7 @@ class ServerSession(Session):
"Initiate to avoid AttributeErrors down the line"
self.puppet = None
self.player = None
self.browserid = None
self.cmdset_storage_string = ""
self.cmdset = CmdSetHandler(self, True)
@ -220,6 +224,13 @@ class ServerSession(Session):
self.puppet = None
self.cmdset_storage = settings.CMDSET_SESSION
if self.browserid:
# this is only set by a webclient inputcommand.
bsession = BrowserSessionStore(session_key=self.browserid)
bsession["logged_in"] = player.id # this also saves the bsession
bsession.save()
print ("serversession.login:", bsession.session_key)
# Update account's last login time.
self.player.last_login = timezone.now()
self.player.save()

View file

@ -369,6 +369,10 @@ class ServerSessionHandler(SessionHandler):
"""
if session.logged_in:
# don't log in a session that is already logged in.
return
# we have to check this first before uid has been assigned
# this session.
@ -589,6 +593,17 @@ class ServerSessionHandler(SessionHandler):
return sessions[0] if len(sessions) == 1 else sessions
sessions_from_character = sessions_from_puppet
def sessions_from_browserid(self, browserid):
"""
Given a browserid, return all sessions having this id.
Args
browserid (str): The browserid hash
"""
return [session for session in self.values()
if session.browserid and session.browserid == browserid]
def announce_all(self, message):
"""
Send message to all connected sessions

View file

@ -230,6 +230,7 @@ An "emitter" object must have a function
open = true;
ever_open = true;
Evennia.emit('connection_open', ["websocket"], event);
Evennia.msg('browser_sessid', [browser_sessid], {});
};
// Handle Websocket close event
websocket.onclose = function (event) {
@ -308,6 +309,7 @@ An "emitter" object must have a function
success: function(data) {
data = JSON.parse(data);
log ("connection_open", ["AJAX/COMET"], data);
Evennia.msg("browser_sessid", [browser_sessid], {});
client_hash = data.suid;
stop_polling = false;
poll();

View file

@ -35,6 +35,12 @@ JQuery available.
var wsactive = false;
{% endif %}
{% if browser_sessid %}
var browser_sessid = "{{browser_sessid}}";
{% else %}
var browser_sessid = false;
{% endif %}
{% if websocket_url %}
var wsurl = "{{websocket_url}}:{{websocket_port}}";
{% else %}

View file

@ -6,7 +6,9 @@ page and serve it eventual static content.
"""
from __future__ import print_function
from django.shortcuts import render
from django.contrib.auth import login
from evennia.server.sessionhandler import SESSION_HANDLER
from evennia.players.models import PlayerDB
@ -14,9 +16,31 @@ def webclient(request):
"""
Webclient page template loading.
"""
print ("webclient session:", request.session.session_key, request.user, request.user.is_authenticated())
nsess = len(PlayerDB.objects.get_connected_players()) or "none"
# as an example we send the number of connected players to the template
pagevars = {'num_players_connected': nsess}
browser_session = request.session
browserid = 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_browserid(browserid):
print ("session to connect:", session)
if session.protocol_key in ("websocket", "ajax/comet"):
SESSION_HANDLER.login(session, player)
session.browserid = browser_session.session_key
browser_session["logged_in"] = player.id
elif browser_session.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(browser_session.get("uid"))
login(player, request)
else:
browser_session["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}
return render(request, 'webclient.html', pagevars)

View file

@ -14,6 +14,8 @@ from evennia import SESSION_HANDLER
from evennia.objects.models import ObjectDB
from evennia.players.models import PlayerDB
from django.contrib.auth import login
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
@ -21,6 +23,33 @@ def page_index(request):
"""
Main root page.
"""
# handle webclient-website shared login
browser_session = request.session
browserid = request.session.session_key
player = request.user
# check if user has authenticated to website
if player.is_authenticated():
# Try to login all the player's webclient sessions - only
# unloggedin ones will actually be logged in.
print "website: player auth, trying to connect sessions"
for session in SESSION_HANDLER.sessions_from_browserid(browserid):
print "session to connect:", session
if session.protocol_key in ("websocket", "ajax/comet"):
SESSION_HANDLER.login(session, player)
session.browserid = browser_session.session_key
browser_session["logged_in"] = player.id
elif browser_session.get("logged_in"):
# The webclient has previously registered a login to this browser_session
print "website: browser_session logged in, trying to login"
player = PlayerDB.objects.get(id=browser_session.get("logged_in"))
login(request, player)
else:
browser_session["logged_in"] = None
print ("website session:", request.session.session_key, request.user, request.user.is_authenticated())
# Some misc. configurable stuff.
# TODO: Move this to either SQL or settings.py based configuration.
fpage_player_limit = 4