Add unittests to email_login contrib and also update it to work correctly with latest code. Pertains to #1105.

This commit is contained in:
Griatch 2017-02-19 14:35:42 +01:00
parent e9a6465c65
commit 1b9016d26a
3 changed files with 67 additions and 78 deletions

View file

@ -12,7 +12,7 @@ from evennia.objects.models import ObjectDB
from evennia.server.models import ServerConfig from evennia.server.models import ServerConfig
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
from evennia.utils import create, logger, utils, ansi from evennia.utils import create, logger, utils
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)
@ -516,12 +516,12 @@ class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
self.session.sessionhandler.session_portal_sync(self.session) self.session.sessionhandler.session_portal_sync(self.session)
def _create_player(session, playername, password, permissions, typeclass=None): def _create_player(session, playername, password, permissions, typeclass=None, email=None):
""" """
Helper function, creates a player of the specified typeclass. Helper function, creates a player of the specified typeclass.
""" """
try: try:
new_player = create.create_player(playername, None, password, permissions=permissions, typeclass=typeclass) new_player = create.create_player(playername, email, password, permissions=permissions, typeclass=typeclass)
except Exception as e: except Exception as e:
session.msg("There was an error creating the Player:\n%s\n If this problem persists, contact an admin." % e) session.msg("There was an error creating the Player:\n%s\n If this problem persists, contact an admin." % e)
@ -535,7 +535,7 @@ def _create_player(session, playername, password, permissions, typeclass=None):
# join the new player to the public channel # join the new player to the public channel
pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"]) pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"])
if not pchannel.connect(new_player): if not pchannel or not pchannel.connect(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_err(string) logger.log_err(string)
return new_player return new_player

View file

@ -118,7 +118,7 @@ class CmdUnconnectedConnect(MuxCommand):
# actually do the login. This will call all hooks. # actually do the login. This will call all hooks.
session.sessionhandler.login(session, player) session.sessionhandler.login(session, player)
from evennia.commands.default import unloggedin as default_unloggedin
class CmdUnconnectedCreate(MuxCommand): class CmdUnconnectedCreate(MuxCommand):
""" """
Create a new account. Create a new account.
@ -161,108 +161,86 @@ class CmdUnconnectedCreate(MuxCommand):
"""Do checks and create account""" """Do checks and create account"""
session = self.caller session = self.caller
try: try:
playername, email, password = self.playerinfo playername, email, password = self.playerinfo
except ValueError: except ValueError:
string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>" string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>"
session.msg(string) session.msg(string)
return return
if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
session.msg("\n\r Playername can be max 30 characters, or less. Letters, spaces,"
" digits and @/./+/-/_ only.") # this echoes the restrictions made by django's auth module.
return
if not email or not password: if not email or not password:
session.msg("\n\r You have to supply an e-mail address followed by a password.") session.msg("\n\r You have to supply an e-mail address followed by a password.")
return return
if not utils.validate_email_address(email): if not utils.validate_email_address(email):
# check so the email at least looks ok. # check so the email at least looks ok.
session.msg("'%s' is not a valid e-mail address." % email) session.msg("'%s' is not a valid e-mail address." % email)
return return
# sanity checks
# Run sanity and security checks if not re.findall(r"^[\w. @+\-']+$", playername) or not (0 < len(playername) <= 30):
# this echoes the restrictions made by django's auth
if PlayerDB.objects.filter(username=playername): # module (except not allowing spaces, for convenience of
# player already exists # logging in).
string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_/' only."
session.msg(string)
return
# strip excessive spaces in playername
playername = re.sub(r"\s+", " ", playername).strip()
if PlayerDB.objects.filter(username__iexact=playername):
# player already exists (we also ignore capitalization here)
session.msg("Sorry, there is already a player with the name '%s'." % playername) session.msg("Sorry, there is already a player with the name '%s'." % playername)
return return
if PlayerDB.objects.get_player_from_email(email): if PlayerDB.objects.get_player_from_email(email):
# email already set on a player # email already set on a player
session.msg("Sorry, there is already a player with that email address.") session.msg("Sorry, there is already a player with that email address.")
return return
if len(password) < 3: # Reserve playernames found in GUEST_LIST
# too short password if settings.GUEST_LIST and playername.lower() in (guest.lower() for guest in settings.GUEST_LIST):
string = "Your password must be at least 3 characters or longer." string = "\n\r That name is reserved. Please choose another Playername."
string += "\n\rFor best security, make it at least 8 characters long, "
string += "avoid making it a real word and mix numbers into it."
session.msg(string) session.msg(string)
return return
if not re.findall(r"^[\w. @+\-']+$", password) or not (3 < len(password)):
string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @/./+/-/_/' only." \
"\nFor best security, make it longer than 8 characters. You can also use a phrase of" \
"\nmany words if you enclose the password in double quotes."
session.msg(string)
return
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == playername.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 player account. # everything's ok. Create the new player account.
try: try:
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT permissions = settings.PERMISSION_PLAYER_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
try: new_player = default_unloggedin._create_player(session, playername, password, permissions, email=email)
new_player = create.create_player(playername, email, password, permissions=permissions) if new_player:
if MULTISESSION_MODE < 2:
except Exception as e: default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
session.msg("There was an error creating the default Player/Character:\n%s\n" default_unloggedin._create_character(session, new_player, typeclass, default_home, permissions)
" If this problem persists, contact an admin." % e) # tell the caller everything went well.
logger.log_trace() string = "A new account '%s' was created. Welcome!"
return if " " in playername:
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
# This needs to be set so the engine knows this player is else:
# logging in for the first time. (so it knows to call the right string += "\n\nYou can now log with the command 'connect %s <your password>'."
# hooks during login later) session.msg(string % (playername, email))
new_player.db.FIRST_LOGIN = True
# join the new player to the public channel
pchanneldef = settings.CHANNEL_PUBLIC
if pchanneldef:
pchannel = ChannelDB.objects.get_channel(pchanneldef[0])
if not pchannel.connect(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_err(string)
if MULTISESSION_MODE < 2:
# if we only allow one character, create one with the same name as Player
# (in mode 2, the character must be created manually once logging in)
new_character = create.create_object(typeclass, key=playername,
location=default_home, home=default_home,
permissions=permissions)
# set playable character list
new_player.db._playable_characters.append(new_character)
# allow only the character itself and the player to puppet this character (and Immortals).
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, new_player.id))
# If no description is set, set a default description
if not new_character.db.desc:
new_character.db.desc = "This is a Player."
# We need to set this to have @ic auto-connect to this character
new_player.db._last_puppet = new_character
# tell the caller everything went well.
string = "A new account '%s' was created. Welcome!"
if " " in playername:
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 % (playername, email))
except Exception: except Exception:
# We are in the middle between logged in and -not, so we have # We are in the middle between logged in and -not, so we have
# to handle tracebacks ourselves at this point. If we don't, # to handle tracebacks ourselves at this point. If we don't,
# we won't see any errors at all. # we won't see any errors at all.
session.msg("An error occurred. Please e-mail an admin if the problem persists.") raise
session.msg("%sAn error occurred. Please e-mail an admin if the problem persists.")
logger.log_trace() logger.log_trace()
class CmdUnconnectedQuit(MuxCommand): class CmdUnconnectedQuit(MuxCommand):
""" """
We maintain a different version of the `quit` command We maintain a different version of the `quit` command
@ -276,8 +254,7 @@ class CmdUnconnectedQuit(MuxCommand):
def func(self): def func(self):
"""Simply close the connection.""" """Simply close the connection."""
session = self.caller session = self.caller
session.msg("Good bye! Disconnecting ...") session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
session.session_disconnect()
class CmdUnconnectedLook(MuxCommand): class CmdUnconnectedLook(MuxCommand):

View file

@ -489,6 +489,18 @@ class TestDice(CommandTest):
self.call(dice.CmdDice(), "100000d1000", "The maximum roll allowed is 10000d10000.") self.call(dice.CmdDice(), "100000d1000", "The maximum roll allowed is 10000d10000.")
self.call(dice.CmdDice(), "/secret 3d6 + 4", "You roll 3d6 + 4 (secret, not echoed).") self.call(dice.CmdDice(), "/secret 3d6 + 4", "You roll 3d6 + 4 (secret, not echoed).")
# Test email-login
from evennia.contrib import email_login
class TestEmailLogin(CommandTest):
def test_connect(self):
self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test", "The email 'mytest@test.com' does not match any accounts.")
self.call(email_login.CmdUnconnectedCreate(), '"mytest" mytest@test.com test11111', "A new account 'mytest' was created. Welcome!")
self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test11111", "", caller=self.player.sessions.get()[0])
def test_quit(self):
self.call(email_login.CmdUnconnectedQuit(), "", "", caller=self.player.sessions.get()[0])
def test_unconnectedlook(self):
self.call(email_login.CmdUnconnectedLook(), "", "==========")
def test_unconnectedhelp(self):
self.call(email_login.CmdUnconnectedHelp(), "", "You are not yet logged into the game.")