phi/src/phi/cli/adm.py

120 lines
3.0 KiB
Python

# -*- encoding: utf-8 -*-
import importlib.resources
from fnmatch import fnmatch
import re
import click
from phi.cli.utils import generate_from_templates
from phi.security import hash_pass
SERVICE_RE = re.compile(r"^(.+?):(.+)$")
templates = [
f.name.strip(".j2")
for f in importlib.resources.files("phi.cli.templates").iterdir()
if not f.name.endswith(".py")
]
def debug(ctx, out):
if ctx.obj["debug"]:
click.echo(f"DEBUG: {out}", err=True)
def validate_services(ctx, _, value):
for service in value:
if not SERVICE_RE.match(service):
click.echo(f"Unparsable service '{service}' - must match /^(.+?):(.+)$/")
ctx.exit()
return value
@click.group(
name="phiadm", help="This cli may be used to interact with a local phid instance"
)
@click.option("-d", "--debug", is_flag=True, help="Enable debugging information")
@click.pass_context
def cli(ctx, debug):
ctx.ensure_object(dict)
ctx.obj["debug"] = debug
@cli.command(
"generate",
help="The base name of this LDAP directory (e.g. 'dc=example,dc=com')",
)
@click.option(
"-t",
"--template",
type=click.STRING,
multiple=True,
help=f"Name of the template (allowed: {', '.join(templates)};"
+ " accepts also glob-like patterns)",
)
@click.option(
"-s",
"--default-service",
type=click.STRING,
multiple=True,
callback=validate_services,
help="A pair <name>:<password> representing a service (and the associated password)"
)
@click.option(
"--root-password",
type=click.STRING,
required=True,
help="The cleartext password for the root user",
)
@click.option(
"--phi-password",
type=click.STRING,
required=True,
help="The cleartext password for the phi service",
)
@click.argument(
"base_dn",
type=click.STRING,
)
@click.pass_context
def generate(ctx, template, default_service, root_password, phi_password, base_dn):
config = get_config(base_dn, phi_password, root_password)
debug(ctx, f"default_service: {default_service}")
if default_service:
add_default_services(config, default_service)
debug(ctx, f"config: {config}")
debug(ctx, f"templates: {templates}")
if not template:
template = templates
for name, content in generate_from_templates(config):
debug(ctx, f"current template: {name}")
if any(fnmatch(name, t) for t in template):
click.echo(content)
def get_config(base_dn, phi_password, root_password):
config = {"default_services": []}
config["base_dn"] = base_dn
config["phi_password"] = hash_pass(phi_password)
config["root_password"] = hash_pass(root_password)
return config
def add_default_services(config, services):
for service in services:
user, password = parse_service(service)
config["default_services"].append(
{"name": user, "password": hash_pass(password)})
def parse_service(service):
res = SERVICE_RE.search(service).groups()
if len(res) != 2:
raise ValueError(res)
return res[0], res[1]