Mini Shell

Direktori : /opt/sharedrads/
Upload File :
Current File : //opt/sharedrads/cmspass.py

#!/opt/imh-python/bin/python3
"""Scans a cPanel account for WordPress and Joomla! configuration files, then
uses whmapi1 to reset database passwords to match what was found in them.
This script logs to /var/log/messages"""

import subprocess
import re
import sys
from argparse import ArgumentParser
from pathlib import Path
from typing import Union
import rads
import logging
from logging.handlers import SysLogHandler
from cpapis import whmapi1, CpAPIError

WP_CONF = re.compile(r"DB_(USER|PASSWORD).*['\"](.+)['\"]")
JM_CONF = re.compile(r"\b(user|password) = '(.+)'")


def setup_logging() -> logging.Logger:
    logger = logging.getLogger('cmspass.py')
    logger.setLevel(logging.DEBUG)
    out_fmt = logging.Formatter(fmt='%(levelname)s: %(message)s')
    log_fmt = logging.Formatter(fmt='cmspass.py: %(levelname)s: %(message)s')
    stdout = logging.StreamHandler(stream=sys.stdout)
    stdout.setFormatter(out_fmt)
    stdout.setLevel(logging.DEBUG)
    logger.addHandler(stdout)
    syslog = SysLogHandler(address='/dev/log')
    syslog.setFormatter(log_fmt)
    syslog.setLevel(logging.WARNING)
    logger.addHandler(syslog)
    return logger


def conf_parse(
    regex: re.Pattern, path: Path
) -> tuple[Union[str, None], Union[str, None]]:
    """Will parse db conf variables"""
    db_user, db_pass = None, None
    with open(path, encoding='utf-8') as file:
        for line in file:
            if match := regex.search(line):
                key, val = match.groups()
                key: str
                val: str
                if key.lower() == 'user':
                    db_user = val
                elif key.lower() == 'password':
                    db_pass = val
    return db_user, db_pass


def set_pass(logger: logging.Logger, cpuser: str, db_user: str, db_pass: str):
    """Will pass the reset variables to the cPanel API"""
    if db_user is None or db_pass is None:
        return
    try:
        whmapi1(
            'set_mysql_password',
            args={'cpuser': cpuser, 'user': db_user, 'password': db_pass},
            check=True,
        )
    except CpAPIError as exc:
        logger.error("Failed to reset password for %s: %s", db_user, exc)
    else:
        logger.warning("Reset password for %s", db_user)


def parse_args() -> str:
    """Will parse input for user and ensure the user exists"""
    parser = ArgumentParser(description=__doc__)
    parser.add_argument('-u', '--user', required=True, help='cPanel username')
    user = parser.parse_args().user
    if not rads.cpuser_safe(user):
        sys.exit(f"{user} does not exist or is restricted")
    return user


def find_files(homedir: str) -> list[Path]:
    """Find all config files"""
    # fmt: off
    cmd = [
        'find', homedir, '-not', '(', '-path', f"{homedir}/mail", '-prune', ')',
        '(', '-name', 'wp-config.php', '-o', '-name', 'configuration.php', ')',
        '-type', 'f', '-print0',
    ]
    # fmt: on
    ret = subprocess.run(
        cmd, stdout=subprocess.PIPE, encoding='utf-8', check=False
    )
    return [Path(x) for x in ret.stdout.split('\0') if x]


def conf_is_joomla(path: Path):
    if path.name != 'configuration.php':
        return False
    with open(path, encoding='utf-8') as conf:
        for line in conf:
            if 'class JConfig' in line:
                return True
    return False


def main():
    user = parse_args()
    logger = setup_logging()
    try:
        homedir = rads.get_homedir(user)
    except rads.CpuserError as exc:
        sys.exit(exc)
    for path in find_files(homedir):
        db_user, db_pass = None, None
        if path.name == 'wp-config.php':
            logger.debug('Scanning %s', path)
            db_user, db_pass = conf_parse(WP_CONF, path)
        elif conf_is_joomla(path):
            logger.debug('Scanning %s', path)
            db_user, db_pass = conf_parse(JM_CONF, path)
        if db_user and db_pass:
            set_pass(logger, user, db_user, db_pass)


if __name__ == "__main__":
    main()

Zerion Mini Shell 1.0