import inspect import re from typing import Callable class InvalidUsernameException(Exception): pass class InvalidForwardingAddressException(Exception): pass valid_username_re = re.compile(r'^[a-z][\w-]+$') # Only allow usernames and email addresses to be set in ~/.forward valid_forwarding_address_re = re.compile(r'^[\w-]+|[\w.+-]+@[\w-]+(\.[\w-]+)*$') def is_valid_username(username: str) -> bool: """Returns True if the username has a valid format.""" return valid_username_re.match(username) is not None def check_valid_username(f: Callable) -> Callable: """ A decorator which raises an Exception if the username passed to f is invalid. f must accept `username` as a regular argument. """ argspec = inspect.getfullargspec(f) try: arg_idx = argspec.args.index('username') except ValueError: return f def wrapper(*args, **kwargs): username = None if arg_idx < len(args): username = args[arg_idx] elif 'username' in kwargs: username = kwargs['username'] if username is not None and not is_valid_username(username): raise InvalidUsernameException(username) return f(*args, **kwargs) return wrapper def is_valid_forwarding_address(address: str) -> bool: """Returns True if the address is a valid forwarding address.""" return valid_forwarding_address_re.match(address) is not None def is_valid_shell(shell: str) -> bool: """Returns True if this shell is in /etc/shells, otherwise False.""" return shell in [ line.strip() for line in open('/etc/shells') if line != '' and not line.isspace() ] def is_valid_term(term: str) -> bool: return len(term) == 5 and \ term[0] in ['s', 'f', 'w'] and \ term[1:5].isdigit()