mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Make IDLE_TIMEOUT avoidable with account-lock.
Resolves #701. Check the Account-lock 'no_idle_disconnect before kicking an idle session. This also means superusers will never be kicked. Move the idle task to the Server to avoid lock imports in Portal. Make the 'lock' command also able to target Accounts. Also some other fixes.
This commit is contained in:
parent
cdac9678b9
commit
8a2e362b7c
11 changed files with 63 additions and 52 deletions
|
|
@ -190,7 +190,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
|
|||
|
||||
# session-related methods
|
||||
|
||||
def disconnect_session_from_account(self, session):
|
||||
def disconnect_session_from_account(self, session, reason=None):
|
||||
"""
|
||||
Access method for disconnecting a given session from the
|
||||
account (connection happens automatically in the
|
||||
|
|
@ -198,12 +198,13 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
|
|||
|
||||
Args:
|
||||
session (Session): Session to disconnect.
|
||||
reason (str, optional): Eventual reason for the disconnect.
|
||||
|
||||
"""
|
||||
global _SESSIONS
|
||||
if not _SESSIONS:
|
||||
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
|
||||
_SESSIONS.disconnect(session)
|
||||
_SESSIONS.disconnect(session, reason)
|
||||
|
||||
# puppeting operations
|
||||
|
||||
|
|
@ -789,8 +790,8 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
|
|||
|
||||
|
||||
"""
|
||||
reason = reason and "(%s)" % reason or ""
|
||||
self._send_to_connect_channel("|R%s disconnected %s|n" % (self.key, reason))
|
||||
reason = " (%s)" % reason if reason else ""
|
||||
self._send_to_connect_channel("|R%s disconnected%s|n" % (self.key, reason))
|
||||
|
||||
def at_post_disconnect(self, **kwargs):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -657,10 +657,12 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
if 'all' in self.switches:
|
||||
account.msg("|RQuitting|n all sessions. Hope to see you soon again.", session=self.session)
|
||||
reason = "quit/all"
|
||||
for session in account.sessions.all():
|
||||
account.disconnect_session_from_account(session)
|
||||
account.disconnect_session_from_account(session, reason)
|
||||
else:
|
||||
nsess = len(account.sessions.all())
|
||||
reason = "quit"
|
||||
if nsess == 2:
|
||||
account.msg("|RQuitting|n. One session is still connected.", session=self.session)
|
||||
elif nsess > 2:
|
||||
|
|
@ -668,7 +670,7 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
|
|||
else:
|
||||
# we are quitting the last available session
|
||||
account.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session)
|
||||
account.disconnect_session_from_account(self.session)
|
||||
account.disconnect_session_from_account(self.session, reason)
|
||||
|
||||
|
||||
class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||
|
|
|
|||
|
|
@ -1735,9 +1735,9 @@ class CmdLock(ObjManipCommand):
|
|||
assign a lock definition to an object
|
||||
|
||||
Usage:
|
||||
@lock <object>[ = <lockstring>]
|
||||
@lock <object or *account>[ = <lockstring>]
|
||||
or
|
||||
@lock[/switch] <object>/<access_type>
|
||||
@lock[/switch] <object or *account>/<access_type>
|
||||
|
||||
Switch:
|
||||
del - delete given access type
|
||||
|
|
@ -1779,16 +1779,19 @@ class CmdLock(ObjManipCommand):
|
|||
if '/' in self.lhs:
|
||||
# call on the form @lock obj/access_type
|
||||
objname, access_type = [p.strip() for p in self.lhs.split('/', 1)]
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
if objname.startswith("*"):
|
||||
obj = caller.search_account(objname.lstrip('*'))
|
||||
if not obj:
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
if not (obj.access(caller, 'control') or obj.access(caller, "edit")):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
lockdef = obj.locks.get(access_type)
|
||||
string = ""
|
||||
if lockdef:
|
||||
if 'del' in self.switches:
|
||||
if not (obj.access(caller, 'control') or obj.access(caller, "edit")):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
obj.locks.delete(access_type)
|
||||
string = "deleted lock %s" % lockdef
|
||||
else:
|
||||
|
|
@ -1808,9 +1811,12 @@ class CmdLock(ObjManipCommand):
|
|||
return
|
||||
|
||||
objname, lockdef = self.lhs, self.rhs
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
if objname.startswith("*"):
|
||||
obj = caller.search_account(objname.lstrip('*'))
|
||||
if not obj:
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
if not (obj.access(caller, 'control') or obj.access(caller, "edit")):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -73,33 +73,6 @@ AMP_INTERFACE = settings.AMP_INTERFACE
|
|||
AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE
|
||||
|
||||
|
||||
# Maintenance function - this is called repeatedly by the portal.
|
||||
|
||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||
|
||||
|
||||
def _portal_maintenance():
|
||||
"""
|
||||
The maintenance function handles repeated checks and updates that
|
||||
the server needs to do. It is called every minute.
|
||||
|
||||
"""
|
||||
# check for idle sessions
|
||||
now = time.time()
|
||||
|
||||
reason = "Idle timeout exceeded, disconnecting."
|
||||
for session in [sess for sess in PORTAL_SESSIONS.values()
|
||||
if (now - sess.cmd_last) > _IDLE_TIMEOUT]:
|
||||
session.disconnect(reason=reason)
|
||||
PORTAL_SESSIONS.disconnect(session)
|
||||
|
||||
|
||||
if _IDLE_TIMEOUT > 0:
|
||||
# only start the maintenance task if we care about idling.
|
||||
_maintenance_task = LoopingCall(_portal_maintenance)
|
||||
_maintenance_task.start(60) # called every minute
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Portal Service object
|
||||
# -------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -268,7 +268,6 @@ class PortalSessionHandler(SessionHandler):
|
|||
data (dict): The session sync data.
|
||||
|
||||
"""
|
||||
print("server_logged_in: %s: %s" % (session, data))
|
||||
session.load_sync_data(data)
|
||||
session.at_login()
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,12 @@ class SshProtocol(Manhole, session.Session):
|
|||
|
||||
# session-general method hooks
|
||||
|
||||
def at_login(self):
|
||||
"""
|
||||
Called when this session gets authenticated by the server.
|
||||
"""
|
||||
pass
|
||||
|
||||
def disconnect(self, reason="Connection closed. Goodbye for now."):
|
||||
"""
|
||||
Disconnect from server.
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
# do the sync
|
||||
self.sessionhandler.sync(self)
|
||||
|
||||
def at_login(self):
|
||||
"""
|
||||
Called when this session gets authenticated by the server.
|
||||
"""
|
||||
pass
|
||||
|
||||
def enableRemote(self, option):
|
||||
"""
|
||||
This sets up the remote-activated options we allow for this protocol.
|
||||
|
|
|
|||
|
|
@ -107,6 +107,12 @@ class WebClient(resource.Resource):
|
|||
self.keep_alive.stop()
|
||||
self.keep_alive = None
|
||||
|
||||
def at_login(self):
|
||||
"""
|
||||
Called when this session gets authenticated by the server.
|
||||
"""
|
||||
pass
|
||||
|
||||
def lineSend(self, csessid, data):
|
||||
"""
|
||||
This adds the data to the buffer and/or sends it to the client
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import os
|
|||
from twisted.web import static
|
||||
from twisted.application import internet, service
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.task import LoopingCall, deferLater
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
|
@ -36,6 +36,8 @@ from evennia.utils.utils import get_evennia_version, mod_import, make_iter
|
|||
from evennia.comms import channelhandler
|
||||
from evennia.server.sessionhandler import SESSIONS
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
_SA = object.__setattr__
|
||||
|
||||
SERVER_PIDFILE = ""
|
||||
|
|
@ -89,11 +91,13 @@ _FLUSH_CACHE = None
|
|||
_IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE
|
||||
_GAMETIME_MODULE = None
|
||||
|
||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||
|
||||
|
||||
def _server_maintenance():
|
||||
"""
|
||||
This maintenance function handles repeated checks and updates that
|
||||
the server needs to do. It is called every 5 minutes.
|
||||
the server needs to do. It is called every minute.
|
||||
"""
|
||||
global EVENNIA, _MAINTENANCE_COUNT, _FLUSH_CACHE, _GAMETIME_MODULE
|
||||
if not _FLUSH_CACHE:
|
||||
|
|
@ -124,6 +128,14 @@ def _server_maintenance():
|
|||
# validate channels off-sync with scripts
|
||||
evennia.CHANNEL_HANDLER.update()
|
||||
|
||||
# handle idle timeouts
|
||||
reason = _("idle timeout exceeded")
|
||||
for session in (sess for sess in SESSIONS.values()
|
||||
if (now - sess.cmd_last) > _IDLE_TIMEOUT):
|
||||
if not session.account or not \
|
||||
session.account.access(session.account, "no_idle_disconnect", default=False):
|
||||
SESSIONS.disconnect(session, reason=reason)
|
||||
|
||||
# Commenting this out, it is probably not needed
|
||||
# with CONN_MAX_AGE set. Keeping it as a reminder
|
||||
# if database-gone-away errors appears again /Griatch
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ are stored on the Portal side)
|
|||
from builtins import object
|
||||
|
||||
import weakref
|
||||
import importlib
|
||||
import time
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
|
@ -232,7 +231,7 @@ class ServerSession(Session):
|
|||
# add the session-level cmdset
|
||||
self.cmdset = CmdSetHandler(self, True)
|
||||
|
||||
def at_disconnect(self):
|
||||
def at_disconnect(self, reason=None):
|
||||
"""
|
||||
Hook called by sessionhandler when disconnecting this session.
|
||||
|
||||
|
|
@ -245,7 +244,7 @@ class ServerSession(Session):
|
|||
uaccount.last_login = timezone.now()
|
||||
uaccount.save()
|
||||
# calling account hook
|
||||
account.at_disconnect()
|
||||
account.at_disconnect(reason)
|
||||
self.logged_in = False
|
||||
if not self.sessionhandler.sessions_from_account(account):
|
||||
# no more sessions connected to this account
|
||||
|
|
|
|||
|
|
@ -500,11 +500,12 @@ class ServerSessionHandler(SessionHandler):
|
|||
if hasattr(session, "account") and session.account:
|
||||
# only log accounts logging off
|
||||
nsess = len(self.sessions_from_account(session.account)) - 1
|
||||
string = "Logged out: {account} {address} ({nsessions} sessions(s) remaining)"
|
||||
string = string.format(account=session.account, address=session.address, nsessions=nsess)
|
||||
sreason = " ({})".format(reason) if reason else ""
|
||||
string = "Logged out: {account} {address} ({nsessions} sessions(s) remaining){reason}"
|
||||
string = string.format(reason=sreason, account=session.account, address=session.address, nsessions=nsess)
|
||||
session.log(string)
|
||||
|
||||
session.at_disconnect()
|
||||
session.at_disconnect(reason)
|
||||
sessid = session.sessid
|
||||
if sessid in self and not hasattr(self, "_disconnect_all"):
|
||||
del self[sessid]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue