diff --git a/cwopamud/commands/cwopa/__init__.py b/cwopamud/commands/cwopa/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cwopamud/commands/cwopa/admin.py b/cwopamud/commands/cwopa/admin.py new file mode 100644 index 0000000..da5514d --- /dev/null +++ b/cwopamud/commands/cwopa/admin.py @@ -0,0 +1,133 @@ +""" +Commands - CWOPA MUD Admin + +Custom commands for an admin account on the CWOPA MUD. +""" + +import re +import secrets +import string + +from django.conf import settings +from evennia.utils import class_from_module + + +COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) + +_ALPHABET = string.ascii_letters + string.digits + + +def generate_password(length: int = 16) -> str: + """Creates a cryptographically safe password.""" + return "".join(secrets.choice(_ALPHABET) for i in range(length)) + + +class CmdCwopaAdminCreate(COMMAND_DEFAULT_CLASS): + """ + manually create a new account as an admin + + Usage: + admincreate [] + + This creates a new account for someone else as an admin. This is useful + for situtions where registration is disabled but you still want to make + accounts. If a password is not provided, a random 16-character password + will be provided. If there are spaces in the username or password, wrap + it in double quotes. + """ + + key = "admincreate" + aliases = ["adcre", "adcr"] + locks = "cmd:perm(admincreate) or perm(Admin)" + help_category = "Admin" + arg_regex = r"\s.*?|$" + + def func(self): + """Create a new account (with character).""" + + session = self.caller + args = self.args.strip() + + # Grab the account class + Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS) + + # extract double quoted parts + parts = [part.strip() for part in re.split(r"\"", args) if part.strip()] + if '"' in args: + if len(parts) == 1: + username = parts[0] + password = generate_password() + elif len(parts) == 2: + username = parts[0] + password = parts[1] + else: + usage = ( + "\n Usage (without <>): admincreate " + "\nIf or contains spaces, enclose them in double quotes." + ) + session.msg(usage) + return + else: + if len(parts) == 1: + parts = parts[0].split(None) + if len(parts) == 1: + username = parts[0] + password = generate_password() + elif len(parts) == 2: + username = parts[0] + password = parts[1] + else: + usage = ( + "\n Usage (without <>): admincreate " + "\nIf or contains spaces, enclose " + "them in double quotes." + ) + session.msg(usage) + return + else: + usage = ( + "\n Usage (without <>): admincreate " + "\nIf or contains spaces, enclose them " + "in double quotes." + ) + session.msg(usage) + return + + # pre-normalize username so the user know what they get + non_normalized_username = username + username = Account.normalize_username(username) + if non_normalized_username != username: + session.msg( + "Note: your username was normalized to strip spaces and remove " + "characters that could be visually confusing." + ) + + # have the user verify their new account was what they intended + answer = yield ( + f"You want to create an account '{username}' with password " + "'{password}'.\nIs this what you intended? [Y]/N?" + ) + if answer.lower() in ("n", "no"): + session.msg( + "Aborted. If your user name contains spaces, surround it by quotes." + ) + return + + # everything's ok. Create the new player account. + account, errors = Account.create(username=username, password=password, ip="") + if account: + # tell the caller everything went well. + success = "A new account '%s' was created." + if " " in username: + success += ( + "\n\nYou can now log in with the command " + "'connect \"%s\" '." + ) + else: + success += ( + "\n\nYou can now log with the command " + "'connect %s '." + ) + session.msg(success % (username, username)) + else: + session.msg("|R%s|n" % "\n".join(errors)) diff --git a/cwopamud/commands/default_cmdsets.py b/cwopamud/commands/default_cmdsets.py index fee80c7..cb8bf2c 100644 --- a/cwopamud/commands/default_cmdsets.py +++ b/cwopamud/commands/default_cmdsets.py @@ -16,6 +16,8 @@ own cmdsets by inheriting from them or directly from `evennia.CmdSet`. from evennia import default_cmds +from .cwopa.admin import CmdCwopaAdminCreate + class CharacterCmdSet(default_cmds.CharacterCmdSet): """ @@ -54,6 +56,7 @@ class AccountCmdSet(default_cmds.AccountCmdSet): # # any commands you add below will overload the default ones. # + self.add(CmdCwopaAdminCreate) class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):