"""Tinode command line macro definitions.""" import argparse import tn_globals from tn_globals import stdoutln class Macro: """Macro base class. The external callers are expected to access * self.parser - an instance of argparse.Argument parser which attempts to turn a list of tokens into the corresponding argparse command. * self.run() - executes the macro as instructed by the user.""" def __init__(self): self.parser = argparse.ArgumentParser(prog=self.name(), description=self.description()) self.add_parser_args() # Explain argument. self.parser.add_argument('--explain', action='store_true', help='Only print out expanded macro') def name(self): """Macro name.""" pass def description(self): """Macro description.""" pass def add_parser_args(self): """Method which adds custom command line arguments.""" pass def expand(self, id, cmd, args): """Expands the macro to a list of basic Tinode CLI commands.""" pass def run(self, id, cmd, args): """Expands the macro and returns the list of commands to actually execute to the caller depending on the presence of the --explain argument. """ cmds = self.expand(id, cmd, args) if cmd.explain: if cmds is None: return None for item in cmds: stdoutln(item) return [] return cmds class Usermod(Macro): """Modifies user account. The following modes are available: * suspend/unsuspend account. * change user's theCard (public name, description, avatar), private comment, trusted values. This macro requires root privileges.""" def name(self): return "usermod" def description(self): return 'Modify user account (requires root privileges)' def add_parser_args(self): self.parser.add_argument('userid', help='user to update') self.parser.add_argument('-L', '--suspend', action='store_true', help='Suspend account') self.parser.add_argument('-U', '--unsuspend', action='store_true', help='Unsuspend account') self.parser.add_argument('--name', help='Public name') self.parser.add_argument('--avatar', help='Avatar file name') self.parser.add_argument('--comment', help='Private comment on account') self.parser.add_argument('--note', help='Account description') self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger') def expand(self, id, cmd, args): if not cmd.userid: return None # Suspend/unsuspend user. if cmd.suspend or cmd.unsuspend: if cmd.suspend and cmd.unsuspend: stdoutln("Cannot both suspend and unsuspend account") return None new_cmd = 'acc --user %s --as_root' % cmd.userid if cmd.suspend: new_cmd += ' --suspend true' if cmd.unsuspend: new_cmd += ' --suspend false' return [new_cmd] # Change theCard. varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp' set_cmd = '.must ' + varname + ' set me' if cmd.name is not None: set_cmd += ' --fn="%s"' % cmd.name if cmd.avatar is not None: set_cmd += ' --photo="%s"' % cmd.avatar if cmd.comment is not None: set_cmd += ' --private="%s"' % cmd.comment if cmd.note is not None: set_cmd += ' --note="%s"' % cmd.note if cmd.trusted is not None: set_cmd += ' --trusted="%s" --as_root' % cmd.trusted old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else '' return ['.use --user %s' % cmd.userid, '.must sub me', set_cmd, '.must leave me', '.use --user "%s"' % old_user] class Resolve(Macro): """Looks up user id by login name and prints it.""" def name(self): return "resolve" def description(self): return "Resolve login and print the corresponding user id" def add_parser_args(self): self.parser.add_argument('login', help='login to resolve') def expand(self, id, cmd, args): if not cmd.login: return None varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp' return ['.must sub fnd', '.must set fnd --public=basic:%s' % cmd.login, '.must %s get fnd --sub' % varname, '.must leave fnd', '.log %s.sub[0].topic' % varname] class Passwd(Macro): """Sets user's password (requires root privileges).""" def name(self): return "passwd" def description(self): return "Set user's password (requires root privileges)" def add_parser_args(self): self.parser.add_argument('userid', help='Id of the user') self.parser.add_argument('-P', '--password', help='New password') def expand(self, id, cmd, args): if not cmd.userid: return None if not cmd.password: stdoutln("Password (-P) not specified") return None return ['acc --user %s --scheme basic --secret :%s' % (cmd.userid, cmd.password)] class Useradd(Macro): """Creates a new user account.""" def name(self): return "useradd" def description(self): return "Create a new user account" def add_parser_args(self): self.parser.add_argument('login', help='User login') self.parser.add_argument('-P', '--password', help='Password') self.parser.add_argument('--cred', help='List of comma-separated credentials in format "(email|tel):value1,(email|tel):value2,..."') self.parser.add_argument('--name', help='Public name of the user') self.parser.add_argument('--comment', help='Private comment') self.parser.add_argument('--note', help='Public description') self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger') self.parser.add_argument('--tags', help='Comma-separated list of tags') self.parser.add_argument('--avatar', help='Path to avatar file') self.parser.add_argument('--auth', help='Default auth acs') self.parser.add_argument('--anon', help='Default anon acs') def expand(self, id, cmd, args): if not cmd.login: return None if not cmd.password: stdoutln("Password --password must be specified") return None if not cmd.cred: stdoutln("Must specify at least one credential: --cred.") return None varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp' new_cmd = '.must ' + varname + ' acc --scheme basic --secret="%s:%s" --cred="%s"' % (cmd.login, cmd.password, cmd.cred) if cmd.name: new_cmd += ' --fn="%s"' % cmd.name if cmd.comment: new_cmd += ' --private="%s"' % cmd.comment if cmd.note is not None: set_cmd += ' --note="%s"' % cmd.note if cmd.trusted is not None: set_cmd += ' --trusted="%s" --as_root' % cmd.trusted if cmd.tags: new_cmd += ' --tags="%s"' % cmd.tags if cmd.avatar: new_cmd += ' --photo="%s"' % cmd.avatar if cmd.auth: new_cmd += ' --auth="%s"' % cmd.auth if cmd.anon: new_cmd += ' --anon="%s"' % cmd.anon return [new_cmd] class Chacs(Macro): """Modifies default acs (permissions) on a user account.""" def name(self): return "chacs" def description(self): return "Change default permissions/acs for a user (requires root privileges)" def add_parser_args(self): self.parser.add_argument('userid', help='User id') self.parser.add_argument('--auth', help='New auth acs value') self.parser.add_argument('--anon', help='New anon acs value') def expand(self, id, cmd, args): if not cmd.userid: return None if not cmd.auth and not cmd.anon: stdoutln('Must specify at least either of --auth, --anon') return None set_cmd = '.must set me' if cmd.auth: set_cmd += ' --auth=%s' % cmd.auth if cmd.anon: set_cmd += ' --anon=%s' % cmd.anon old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else '' return ['.use --user %s' % cmd.userid, '.must sub me', set_cmd, '.must leave me', '.use --user "%s"' % old_user] class Userdel(Macro): """Deletes a user account.""" def name(self): return "userdel" def description(self): return "Delete user account (requires root privileges)" def add_parser_args(self): self.parser.add_argument('userid', help='User id') self.parser.add_argument('--hard', action='store_true', help='Hard delete') def expand(self, id, cmd, args): if not cmd.userid: return None del_cmd = 'del user --user %s --as_root' % cmd.userid if cmd.hard: del_cmd += ' --hard' return [del_cmd] class Chcred(Macro): """Adds, deletes or validates credentials for a user account.""" def name(self): return "chcred" def description(self): return "Add/delete/validate credentials (requires root privileges)" def add_parser_args(self): self.parser.add_argument('userid', help='User id') self.parser.add_argument('cred', help='Affected credential in formt method:value, e.g. email: abc@example.com, tel:17771112233') self.parser.add_argument('--add', action='store_true', help='Add credential') self.parser.add_argument('--rm', action='store_true', help='Delete credential') self.parser.add_argument('--validate', action='store_true', help='Validate credential') def expand(self, id, cmd, args): if not cmd.userid: return None if not cmd.cred: stdoutln('Must specify cred') return None num_actions = (1 if cmd.add else 0) + (1 if cmd.rm else 0) + (1 if cmd.validate else 0) if num_actions == 0 or num_actions > 1: stdoutln('Must specify exactly one action: --add, --rm, --validate') return None if cmd.add: cred_cmd = '.must set me --cred %s' % cmd.cred if cmd.rm: cred_cmd = '.must del --topic me --cred %s cred' % cmd.cred old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else '' return ['.use --user %s' % cmd.userid, '.must sub me', cred_cmd, '.must leave me', '.use --user "%s"' % old_user] class Thecard(Macro): """Prints user's theCard.""" def name(self): return "thecard" def description(self): return "Print theCard for a user (requires root privileges)" def add_parser_args(self): self.parser.add_argument('userid', help='User id') self.parser.add_argument('--what', choices=['desc', 'cred'], required=True, help='Type of data to print (desc - public/private data, cred - list of credentials.') def expand(self, id, cmd, args): if not cmd.userid: return None varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp' old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else '' return ['.use --user %s' % cmd.userid, '.must sub me', '.must %s get me --%s' % (varname, cmd.what), '.must leave me', '.use --user "%s"' % old_user, '.log %s' % varname] def parse_macro(parts): """Attempts to find a parser for the provided sequence of tokens.""" global Macros # parts[0] is the command. macro = Macros.get(parts[0]) if not macro: return None return macro.parser Macros = {x.name(): x for x in [Usermod(), Resolve(), Passwd(), Useradd(), Chacs(), Userdel(), Chcred(), Thecard()]}