Implements create() and authenticate() on DefaultGuest object; migrates DefaultAccount.authenticate_guest().

This commit is contained in:
Johnny 2018-10-10 00:41:27 +00:00
parent f943b3e179
commit d78cd56279
2 changed files with 108 additions and 102 deletions

View file

@ -413,82 +413,6 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
raise ImproperlyConfigured(msg % validator['NAME']) raise ImproperlyConfigured(msg % validator['NAME'])
objs.append(klass(**validator.get('OPTIONS', {}))) objs.append(klass(**validator.get('OPTIONS', {})))
return objs return objs
@classmethod
def authenticate_guest(cls, **kwargs):
"""
Gets or creates a Guest account object.
Kwargs:
ip (str, optional): IP address of requestor; used for ban checking,
throttling and logging
"""
errors = []
account = None
username = None
ip = kwargs.get('ip', '').strip()
# check if guests are enabled.
if not settings.GUEST_ENABLED:
errors.append('Guest accounts are not enabled on this server.')
return None, errors
# See if authentication is currently being throttled
if ip and LOGIN_THROTTLE.check(ip):
errors.append('Too many login failures; please try again in a few minutes.')
# With throttle active, do not log continued hits-- it is a
# waste of storage and can be abused to make your logs harder to
# read and/or fill up your disk.
return None, errors
# check if IP banned
if ip and cls.is_banned(ip=ip):
errors.append("|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x")
logger.log_sec('Authentication Denied (Banned): %s (IP: %s).' % ('guest', ip))
LOGIN_THROTTLE.update(ip, 'Too many sightings of banned IP.')
return None, errors
try:
# Find an available guest name.
for name in settings.GUEST_LIST:
if not AccountDB.objects.filter(username__iexact=name).count():
username = name
break
if not username:
errors.append("All guest accounts are in use. Please try again later.")
if ip: LOGIN_THROTTLE.update(ip, 'Too many requests for Guest access.')
return None, errors
else:
# build a new account with the found guest username
password = "%016x" % getrandbits(64)
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
permissions = settings.PERMISSION_GUEST_DEFAULT
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
account_typeclass = settings.BASE_GUEST_TYPECLASS
account, errs = cls.create(
guest=True,
username=username,
password=password,
permissions=permissions,
account_typeclass=account_typeclass,
character_typeclass=character_typeclass,
ip=ip,
)
errors.extend(errs)
return account, errors
except Exception:
# We are in the middle between logged in and -not, so we have
# to handle tracebacks ourselves at this point. If we don't,
# we won't see any errors at all.
errors.append("An error occurred. Please e-mail an admin if the problem persists.")
logger.log_trace()
return None, errors
return account, errors
@classmethod @classmethod
def authenticate(cls, username, password, ip='', **kwargs): def authenticate(cls, username, password, ip='', **kwargs):
@ -700,7 +624,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
guest (bool, optional): Whether or not this is to be a Guest account guest (bool, optional): Whether or not this is to be a Guest account
permissions (str, optional): Default permissions for the Account permissions (str, optional): Default permissions for the Account
account_typeclass (str, optional): Typeclass to use for new Account typeclass (str, optional): Typeclass to use for new Account
character_typeclass (str, optional): Typeclass to use for new char character_typeclass (str, optional): Typeclass to use for new char
when applicable. when applicable.
@ -719,8 +643,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
guest = kwargs.get('guest', False) guest = kwargs.get('guest', False)
permissions = kwargs.get('permissions', settings.PERMISSION_ACCOUNT_DEFAULT) permissions = kwargs.get('permissions', settings.PERMISSION_ACCOUNT_DEFAULT)
account_typeclass = kwargs.get('account_typeclass', settings.BASE_ACCOUNT_TYPECLASS) typeclass = kwargs.get('typeclass', settings.BASE_ACCOUNT_TYPECLASS)
character_typeclass = kwargs.get('character_typeclass', settings.BASE_CHARACTER_TYPECLASS)
ip = kwargs.get('ip', '') ip = kwargs.get('ip', '')
if ip and CREATION_THROTTLE.check(ip): if ip and CREATION_THROTTLE.check(ip):
@ -759,7 +682,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
# everything's ok. Create the new account account. # everything's ok. Create the new account account.
try: try:
try: try:
account = create.create_account(username, email, password, permissions=permissions, typeclass=account_typeclass) account = create.create_account(username, email, password, permissions=permissions, typeclass=typeclass)
logger.log_sec('Account Created: %s (IP: %s).' % (account, ip)) logger.log_sec('Account Created: %s (IP: %s).' % (account, ip))
except Exception as e: except Exception as e:
@ -784,10 +707,15 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
if account and settings.MULTISESSION_MODE < 2: if account and settings.MULTISESSION_MODE < 2:
# Load the appropriate Character class # Load the appropriate Character class
Character = class_from_module(settings.BASE_CHARACTER_TYPECLASS) character_typeclass = kwargs.get('character_typeclass', settings.BASE_CHARACTER_TYPECLASS)
character_home = kwargs.get('home')
Character = class_from_module(character_typeclass)
# Create the character # Create the character
character, errs = Character.create(account.key, account, ip=ip) character, errs = Character.create(
account.key, account, ip=ip, typeclass=character_typeclass,
permissions=permissions, home=character_home
)
errors.extend(errs) errors.extend(errs)
if character: if character:
@ -1453,6 +1381,78 @@ class DefaultGuest(DefaultAccount):
This class is used for guest logins. Unlike Accounts, Guests and This class is used for guest logins. Unlike Accounts, Guests and
their characters are deleted after disconnection. their characters are deleted after disconnection.
""" """
@classmethod
def create(cls, **kwargs):
"""
Forwards request to cls.authenticate(); returns a DefaultGuest object
if one is available for use.
"""
return cls.authenticate(**kwargs)
@classmethod
def authenticate(cls, **kwargs):
"""
Gets or creates a Guest account object.
Kwargs:
ip (str, optional): IP address of requestor; used for ban checking,
throttling and logging
Returns:
account (Object): Guest account object, if available
errors (list): List of error messages accrued during this request.
"""
errors = []
account = None
username = None
ip = kwargs.get('ip', '').strip()
# check if guests are enabled.
if not settings.GUEST_ENABLED:
errors.append('Guest accounts are not enabled on this server.')
return None, errors
try:
# Find an available guest name.
for name in settings.GUEST_LIST:
if not AccountDB.objects.filter(username__iexact=name).count():
username = name
break
if not username:
errors.append("All guest accounts are in use. Please try again later.")
if ip: LOGIN_THROTTLE.update(ip, 'Too many requests for Guest access.')
return None, errors
else:
# build a new account with the found guest username
password = "%016x" % getrandbits(64)
home = settings.GUEST_HOME
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_GUEST_TYPECLASS
# Call parent class creator
account, errs = super(DefaultGuest, cls).create(
guest=True,
username=username,
password=password,
permissions=permissions,
typeclass=typeclass,
home=home,
ip=ip,
)
errors.extend(errs)
return account, errors
except Exception as e:
# We are in the middle between logged in and -not, so we have
# to handle tracebacks ourselves at this point. If we don't,
# we won't see any errors at all.
errors.append("An error occurred. Please e-mail an admin if the problem persists.")
logger.log_trace()
return None, errors
return account, errors
def at_post_login(self, session=None, **kwargs): def at_post_login(self, session=None, **kwargs):
""" """

View file

@ -6,8 +6,9 @@ from unittest import TestCase
from django.test import override_settings from django.test import override_settings
from evennia.accounts.accounts import AccountSessionHandler from evennia.accounts.accounts import AccountSessionHandler
from evennia.accounts.accounts import DefaultAccount from evennia.accounts.accounts import DefaultAccount, DefaultGuest
from evennia.server.session import Session from evennia.server.session import Session
from evennia.utils.test_resources import EvenniaTest
from evennia.utils import create from evennia.utils import create
from django.conf import settings from django.conf import settings
@ -59,9 +60,31 @@ class TestAccountSessionHandler(TestCase):
def test_count(self): def test_count(self):
"Check count method" "Check count method"
self.assertEqual(self.handler.count(), len(self.handler.get())) self.assertEqual(self.handler.count(), len(self.handler.get()))
class TestDefaultGuest(EvenniaTest):
"Check DefaultGuest class"
ip = '212.216.134.22'
def test_authenticate(self):
# Guest account should not be permitted
account, errors = DefaultGuest.authenticate(ip=self.ip)
self.assertFalse(account, 'Guest account was created despite being disabled.')
settings.GUEST_ENABLED = True
settings.GUEST_LIST = ['bruce_wayne']
# Create a guest account
account, errors = DefaultGuest.authenticate(ip=self.ip)
self.assertTrue(account, 'Guest account should have been created.')
# Create a second guest account
account, errors = DefaultGuest.authenticate(ip=self.ip)
self.assertFalse(account, 'Two guest accounts were created with a single entry on the guest list!')
settings.GUEST_ENABLED = False
class TestDefaultAccount(EvenniaTest):
class TestDefaultAccount(TestCase):
"Check DefaultAccount class" "Check DefaultAccount class"
def setUp(self): def setUp(self):
@ -92,23 +115,6 @@ class TestDefaultAccount(TestCase):
account, errors = DefaultAccount.create(username='Ziggy', password='starman11') account, errors = DefaultAccount.create(username='Ziggy', password='starman11')
self.assertFalse(account, 'Duplicate account name should not have been allowed.') self.assertFalse(account, 'Duplicate account name should not have been allowed.')
# Guest account should not be permitted
account, errors = DefaultAccount.authenticate_guest()
self.assertFalse(account, 'Guest account was created despite being disabled.')
settings.GUEST_ENABLED = True
settings.GUEST_LIST = ['bruce_wayne']
# Create a guest account
account, errors = DefaultAccount.authenticate_guest()
self.assertTrue(account, 'Guest account should have been created.')
# Create a second guest account
account, errors = DefaultAccount.authenticate_guest()
self.assertFalse(account, 'Two guest accounts were created despite a single entry on the guest list!')
settings.GUEST_ENABLED = False
def test_throttle(self): def test_throttle(self):
"Confirm throttle activates on too many failures." "Confirm throttle activates on too many failures."
for x in xrange(20): for x in xrange(20):