Moves account creation logic from Commands module to Account class.

This commit is contained in:
Johnny 2018-10-02 20:23:23 +00:00
parent 16648d47d1
commit 21d66ab625
4 changed files with 354 additions and 176 deletions

View file

@ -23,8 +23,9 @@ from evennia.accounts.models import AccountDB
from evennia.objects.models import ObjectDB from evennia.objects.models import ObjectDB
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
from evennia.commands import cmdhandler from evennia.commands import cmdhandler
from evennia.server.models import ServerConfig
from evennia.server.throttle import Throttle from evennia.server.throttle import Throttle
from evennia.utils import logger from evennia.utils import create, logger
from evennia.utils.utils import (lazy_property, to_str, from evennia.utils.utils import (lazy_property, to_str,
make_iter, to_unicode, is_iter, make_iter, to_unicode, is_iter,
variable_from_module) variable_from_module)
@ -34,6 +35,7 @@ from evennia.commands.cmdsethandler import CmdSetHandler
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from future.utils import with_metaclass from future.utils import with_metaclass
from random import getrandbits
__all__ = ("DefaultAccount",) __all__ = ("DefaultAccount",)
@ -364,6 +366,31 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
puppet = property(__get_single_puppet) puppet = property(__get_single_puppet)
# utility methods # utility methods
@classmethod
def is_banned(cls, **kwargs):
"""
Checks if a given username or IP is banned.
Kwargs:
ip (str, optional): IP address.
username (str, optional): Username.
Returns:
is_banned (bool): Whether either is banned or not.
"""
ip = kwargs.get('ip', '').strip()
username = kwargs.get('username', '').lower().strip()
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == username for tup in bans if username) or
any(tup[2].match(ip) for tup in bans if ip and tup[2])):
return True
return False
@classmethod @classmethod
def get_username_validators(cls, validator_config=getattr(settings, 'AUTH_USERNAME_VALIDATORS', [])): def get_username_validators(cls, validator_config=getattr(settings, 'AUTH_USERNAME_VALIDATORS', [])):
""" """
@ -388,7 +415,83 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
return objs return objs
@classmethod @classmethod
def authenticate(cls, username, password, ip=None): 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
def authenticate(cls, username, password, ip='', **kwargs):
""" """
Checks the given username/password against the database to see if the Checks the given username/password against the database to see if the
credentials are valid. credentials are valid.
@ -408,6 +511,9 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
password (str): Password of account password (str): Password of account
ip (str, optional): IP address of client ip (str, optional): IP address of client
Kwargs:
session (Session, optional): Session requesting authentication
Returns: Returns:
account (DefaultAccount, None): Account whose credentials were account (DefaultAccount, None): Account whose credentials were
provided if not banned. provided if not banned.
@ -423,7 +529,17 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
# With throttle active, do not log continued hits-- it is a # With throttle active, do not log continued hits-- it is a
# waste of storage and can be abused to make your logs harder to # waste of storage and can be abused to make your logs harder to
# read and fill up your disk. # read and/or fill up your disk.
return None, errors
# Check IP and/or name bans
banned = cls.is_banned(username=username, ip=ip)
if banned:
# this is a banned IP or name!
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).' % (username, ip))
LOGIN_THROTTLE.update(ip, 'Too many sightings of banned artifact.')
return None, errors return None, errors
# Authenticate and get Account object # Authenticate and get Account object
@ -436,7 +552,14 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
logger.log_sec('Authentication Failure: %s (IP: %s).' % (username, ip)) logger.log_sec('Authentication Failure: %s (IP: %s).' % (username, ip))
# Update throttle # Update throttle
if ip: LOGIN_THROTTLE.update(ip, 'Too many authentication failures') if ip: LOGIN_THROTTLE.update(ip, 'Too many authentication failures.')
# Try to call post-failure hook
session = kwargs.get('session', None)
if session:
account = AccountDB.objects.get_account_from_name(username)
if account:
account.at_failed_login(session)
return None, errors return None, errors
@ -493,7 +616,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
valid.append(not validator(username)) valid.append(not validator(username))
except ValidationError as e: except ValidationError as e:
valid.append(False) valid.append(False)
[errors.append(x) for x in e.messages] errors.extend(e.messages)
# Disqualify if any check failed # Disqualify if any check failed
if False in valid: if False in valid:
@ -562,6 +685,142 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
logger.log_sec("Password successfully changed for %s." % self) logger.log_sec("Password successfully changed for %s." % self)
self.at_password_change() self.at_password_change()
@classmethod
def create(cls, *args, **kwargs):
"""
Creates an Account (or Account/Character pair for MM<2) with default
(or overridden) permissions and having joined them to the appropriate
default channels.
Kwargs:
username (str): Username of Account owner
password (str): Password of Account owner
email (str, optional): Email address of Account owner
ip (str, optional): IP address of requesting connection
guest (bool, optional): Whether or not this is to be a Guest account
permissions (str, optional): Default permissions for the Account
account_typeclass (str, optional): Typeclass to use for new Account
character_typeclass (str, optional): Typeclass to use for new char
when applicable.
Returns:
account (Account): Account if successfully created; None if not
errors (list): List of error messages in string form
"""
account = None
errors = []
username = kwargs.get('username')
password = kwargs.get('password')
email = kwargs.get('email', '').strip()
guest = kwargs.get('guest', False)
permissions = kwargs.get('permissions', settings.PERMISSION_ACCOUNT_DEFAULT)
account_typeclass = kwargs.get('account_typeclass', settings.BASE_ACCOUNT_TYPECLASS)
character_typeclass = kwargs.get('character_typeclass', settings.BASE_CHARACTER_TYPECLASS)
ip = kwargs.get('ip', '')
if ip and CREATION_THROTTLE.check(ip):
errors.append("You are creating too many accounts. Please log into an existing account.")
return None, errors
# Normalize username
username = cls.normalize_username(username)
# Validate username
if not guest:
valid, errs = cls.validate_username(username)
if not valid:
# this echoes the restrictions made by django's auth
# module (except not allowing spaces, for convenience of
# logging in).
errors.extend(errs)
return None, errors
# Validate password
# Have to create a dummy Account object to check username similarity
valid, errs = cls.validate_password(password, account=cls(username=username))
if not valid:
errors.extend(errs)
return None, errors
# Check IP and/or name bans
banned = cls.is_banned(username=username, ip=ip)
if banned:
# this is a banned IP or name!
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
errors.append(string)
return None, errors
# everything's ok. Create the new account account.
try:
try:
account = create.create_account(username, email, password, permissions=permissions, typeclass=account_typeclass)
logger.log_sec('Account Created: %s (IP: %s).' % (account, ip))
except Exception as e:
errors.append("There was an error creating the Account. If this problem persists, contact an admin.")
logger.log_trace()
return None, errors
# This needs to be set so the engine knows this account is
# logging in for the first time. (so it knows to call the right
# hooks during login later)
account.db.FIRST_LOGIN = True
# Record IP address of creation, if available
if ip: account.db.creator_ip = ip
# join the new account to the public channel
pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"])
if not pchannel or not pchannel.connect(account):
string = "New account '%s' could not connect to public channel!" % account.key
errors.append(string)
logger.log_err(string)
if account:
if settings.MULTISESSION_MODE < 2:
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
try:
character = create.create_object(character_typeclass, key=account.key, home=default_home, permissions=permissions)
# set playable character list
account.db._playable_characters.append(character)
# allow only the character itself and the account to puppet this character (and Developers).
character.locks.add("puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer)" %
(character.id, account.id))
# If no description is set, set a default description
if not character.db.desc:
character.db.desc = "This is a character."
# We need to set this to have @ic auto-connect to this character
account.db._last_puppet = character
# Record creator id and creation IP
if ip: character.db.creator_ip = ip
character.db.creator_id = account.id
except Exception as e:
errors.append("There was an error creating a Character. If this problem persists, contact an admin.")
logger.log_trace()
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()
# Update the throttle to indicate a new account was created from this IP
if ip and not guest: CREATION_THROTTLE.update(ip, 'Too many accounts being created.')
return account, errors
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
""" """
Deletes the account permanently. Deletes the account permanently.

View file

@ -75,6 +75,41 @@ class TestDefaultAccount(TestCase):
obj, errors = DefaultAccount.authenticate(self.account.name, 'xyzzy') obj, errors = DefaultAccount.authenticate(self.account.name, 'xyzzy')
self.assertFalse(obj, 'Account authenticated using invalid credentials.') self.assertFalse(obj, 'Account authenticated using invalid credentials.')
def test_create(self):
"Confirm Account creation is working as expected."
# Create a normal account
account, errors = DefaultAccount.create(username='ziggy', password='stardust11')
self.assertTrue(account, 'New account should have been created.')
# Try creating a duplicate account
account, errors = DefaultAccount.create(username='Ziggy', password='starman11')
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):
"Confirm throttle activates on too many failures."
for x in xrange(20):
obj, errors = DefaultAccount.authenticate(self.account.name, 'xyzzy', ip='12.24.36.48')
self.assertFalse(obj, 'Authentication was provided a bogus password; this should NOT have returned an account!')
self.assertTrue('too many login failures' in errors[-1].lower(), 'Failed logins should have been throttled.')
def test_username_validation(self): def test_username_validation(self):
"Check username validators deny relevant usernames" "Check username validators deny relevant usernames"
# Should not accept Unicode by default, lest users pick names like this # Should not accept Unicode by default, lest users pick names like this
@ -92,7 +127,7 @@ class TestDefaultAccount(TestCase):
def test_password_validation(self): def test_password_validation(self):
"Check password validators deny bad passwords" "Check password validators deny bad passwords"
self.account = create.create_account("TestAccount%s" % randint(0, 9), self.account = create.create_account("TestAccount%s" % randint(100000, 999999),
email="test@test.com", password="testpassword", typeclass=DefaultAccount) email="test@test.com", password="testpassword", typeclass=DefaultAccount)
for bad in ('', '123', 'password', 'TestAccount', '#', 'xyzzy'): for bad in ('', '123', 'password', 'TestAccount', '#', 'xyzzy'):
self.assertFalse(self.account.validate_password(bad, account=self.account)[0]) self.assertFalse(self.account.validate_password(bad, account=self.account)[0])

View file

@ -4,10 +4,9 @@ Commands that are available from the connect screen.
import re import re
import time import time
import datetime import datetime
from random import getrandbits
from django.conf import settings from django.conf import settings
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from evennia.accounts.accounts import CREATION_THROTTLE, LOGIN_THROTTLE
from evennia.accounts.models import AccountDB from evennia.accounts.models import AccountDB
from evennia.objects.models import ObjectDB from evennia.objects.models import ObjectDB
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
@ -15,7 +14,7 @@ from evennia.server.models import ServerConfig
from evennia.server.sessionhandler import SESSIONS from evennia.server.sessionhandler import SESSIONS
from evennia.server.throttle import Throttle from evennia.server.throttle import Throttle
from evennia.utils import create, logger, utils, gametime from evennia.utils import class_from_module, create, logger, utils, gametime
from evennia.commands.cmdhandler import CMD_LOGINSTART from evennia.commands.cmdhandler import CMD_LOGINSTART
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS) COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -27,9 +26,6 @@ __all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
MULTISESSION_MODE = settings.MULTISESSION_MODE MULTISESSION_MODE = settings.MULTISESSION_MODE
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
# Create throttles for too many connections
CONNECTION_THROTTLE = Throttle(limit=5, timeout=1 * 60)
def create_guest_account(session): def create_guest_account(session):
""" """
Creates a guest account/character for this session, if one is available. Creates a guest account/character for this session, if one is available.
@ -42,50 +38,20 @@ def create_guest_account(session):
the boolean is whether guest accounts are enabled at all. the boolean is whether guest accounts are enabled at all.
the Account which was created from an available guest name. the Account which was created from an available guest name.
""" """
# check if guests are enabled. enabled = settings.GUEST_ENABLED
if not settings.GUEST_ENABLED: address = session.address
return False, None
# Check IP bans. # Get account class
bans = ServerConfig.objects.conf("server_bans") Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
if bans and any(tup[2].match(session.address) for tup in bans if tup[2]):
# this is a banned IP!
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return True, None
try:
# Find an available guest name.
accountname = None
for name in settings.GUEST_LIST:
if not AccountDB.objects.filter(username__iexact=accountname).count():
accountname = name
break
if not accountname:
session.msg("All guest accounts are in use. Please try again later.")
return True, None
else:
# build a new account with the found guest accountname
password = "%016x" % getrandbits(64)
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
ptypeclass = settings.BASE_GUEST_TYPECLASS
new_account = _create_account(session, accountname, password, permissions, ptypeclass)
if new_account:
_create_character(session, new_account, typeclass, home, permissions)
return True, new_account
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.
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
logger.log_trace()
raise
# Get an available guest account
# authenticate_guest() handles its own throttling
account, errors = Account.authenticate_guest(ip=address)
if account:
return enabled, account
else:
session.msg("|R%s|n" % '\n'.join(errors))
return enabled, None
def create_normal_account(session, name, password): def create_normal_account(session, name, password):
""" """
@ -99,38 +65,17 @@ def create_normal_account(session, name, password):
Returns: Returns:
account (Account): the account which was created from the name and password. account (Account): the account which was created from the name and password.
""" """
# check for too many login errors too quick. # Get account class
address = session.address Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
if isinstance(address, tuple):
address = address[0]
if LOGIN_THROTTLE.check(address): address = session.address
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
return None
# Match account name and check password # Match account name and check password
account = authenticate(username=name, password=password) # authenticate() handles all its own throttling
account, errors = Account.authenticate(username=name, password=password, ip=address, session=session)
if not account: if not account:
# No accountname or password match # No accountname or password match
session.msg("Incorrect login information given.") session.msg("|R%s|n" % '\n'.join(errors))
# this just updates the throttle
LOGIN_THROTTLE.update(address)
# calls account hook for a failed login if possible.
account = AccountDB.objects.get_account_from_name(name)
if account:
account.at_failed_login(session)
return None
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == account.name.lower() for tup in bans) or
any(tup[2].match(session.address) for tup in bans if tup[2])):
# this is a banned IP or name!
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return None return None
return account return account
@ -162,15 +107,10 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
there is no object yet before the account has logged in) there is no object yet before the account has logged in)
""" """
session = self.caller session = self.caller
# check for too many login errors too quick.
address = session.address address = session.address
if isinstance(address, tuple):
address = address[0] # Get account class
if CONNECTION_THROTTLE.check(address): Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
# timeout is 5 minutes.
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
return
args = self.args args = self.args
# extract double quote parts # extract double quote parts
@ -178,23 +118,27 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
if len(parts) == 1: if len(parts) == 1:
# this was (hopefully) due to no double quotes being found, or a guest login # this was (hopefully) due to no double quotes being found, or a guest login
parts = parts[0].split(None, 1) parts = parts[0].split(None, 1)
# Guest login # Guest login
if len(parts) == 1 and parts[0].lower() == "guest": if len(parts) == 1 and parts[0].lower() == "guest":
enabled, new_account = create_guest_account(session) account, errors = Account.authenticate_guest(ip=address)
if new_account: if account:
session.sessionhandler.login(session, new_account) session.sessionhandler.login(session, account)
if enabled: return
else:
session.msg("|R%s|n" % '\n'.join(errors))
return return
if len(parts) != 2: if len(parts) != 2:
session.msg("\n\r Usage (without <>): connect <name> <password>") session.msg("\n\r Usage (without <>): connect <name> <password>")
return return
CONNECTION_THROTTLE.update(address)
name, password = parts name, password = parts
account = create_normal_account(session, name, password) account, errors = Account.authenticate(username=name, password=password, ip=address, session=session)
if account: if account:
session.sessionhandler.login(session, account) session.sessionhandler.login(session, account)
else:
session.msg("|R%s|n" % '\n'.join(errors))
class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS): class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
@ -220,14 +164,10 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
session = self.caller session = self.caller
args = self.args.strip() args = self.args.strip()
# Rate-limit account creation.
address = session.address address = session.address
if isinstance(address, tuple): # Get account class
address = address[0] Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
if CREATION_THROTTLE.check(address):
session.msg("|RYou are creating too many accounts. Try again in a few minutes.|n")
return
# extract double quoted parts # extract double quoted parts
parts = [part.strip() for part in re.split(r"\"", args) if part.strip()] parts = [part.strip() for part in re.split(r"\"", args) if part.strip()]
@ -239,77 +179,21 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
"\nIf <name> or <password> contains spaces, enclose it in double quotes." "\nIf <name> or <password> contains spaces, enclose it in double quotes."
session.msg(string) session.msg(string)
return return
accountname, password = parts
# sanity checks username, password = parts
if not re.findall(r"^[\w. @+\-']+$", accountname) or not (0 < len(accountname) <= 30):
# this echoes the restrictions made by django's auth
# module (except not allowing spaces, for convenience of
# logging in).
string = "\n\r Accountname can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_/' only."
session.msg(string)
return
# strip excessive spaces in accountname
accountname = re.sub(r"\s+", " ", accountname).strip()
if AccountDB.objects.filter(username__iexact=accountname):
# account already exists (we also ignore capitalization here)
session.msg("Sorry, there is already an account with the name '%s'." % accountname)
return
# Reserve accountnames found in GUEST_LIST
if settings.GUEST_LIST and accountname.lower() in (guest.lower() for guest in settings.GUEST_LIST):
string = "\n\r That name is reserved. Please choose another Accountname."
session.msg(string)
return
# Validate password
Account = utils.class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
# Have to create a dummy Account object to check username similarity
valid, error = Account.validate_password(password, account=Account(username=accountname))
if error:
errors = [e for suberror in error.messages for e in error.messages]
string = "\n".join(errors)
session.msg(string)
return
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == accountname.lower() for tup in bans) or
any(tup[2].match(session.address) for tup in bans if tup[2])):
# this is a banned IP or name!
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return
# everything's ok. Create the new account account. # everything's ok. Create the new account account.
try: account, errors = Account.create(username=username, password=password, ip=address, session=session)
permissions = settings.PERMISSION_ACCOUNT_DEFAULT if account:
typeclass = settings.BASE_CHARACTER_TYPECLASS # tell the caller everything went well.
new_account = _create_account(session, accountname, password, permissions) string = "A new account '%s' was created. Welcome!"
if new_account: if " " in username:
if MULTISESSION_MODE < 2: string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME) else:
_create_character(session, new_account, typeclass, default_home, permissions) string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (username, username))
# Update the throttle to indicate a new account was created from this IP else:
CREATION_THROTTLE.update(address) session.msg("|R%s|n" % '\n'.join(errors))
# tell the caller everything went well.
string = "A new account '%s' was created. Welcome!"
if " " in accountname:
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
else:
string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (accountname, accountname))
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.
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
logger.log_trace()
class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS): class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS):

View file

@ -23,7 +23,7 @@ class EvenniaUsernameAvailabilityValidator:
""" """
# Check guest list # Check guest list
if settings.GUEST_LIST and username.lower() in (guest.lower() for guest in settings.GUEST_LIST): if (settings.GUEST_LIST and username.lower() in (guest.lower() for guest in settings.GUEST_LIST)):
raise ValidationError( raise ValidationError(
_('Sorry, that username is reserved.'), _('Sorry, that username is reserved.'),
code='evennia_username_reserved', code='evennia_username_reserved',