#!/bin/bash
#
# dirctl — global directory management command for the FreeIPA test container.
#
#   dirctl seed                     seed groups + users, save credentials
#   dirctl create [opts]            create a user (all fields optional / auto-generated)
#   dirctl list                     list users
#   dirctl show <username>          show a user
#   dirctl passwd <username> [pw]   set/reset a user password (random if omitted)
#   dirctl update <username> [opts] update user attributes
#
# create/update opts (long or short):
#   -u|--username  -p|--password  -e|--email  -f|--first  -l|--last  -g|--group
# create with no options generates a random username, email and password.
#
set -euo pipefail

REALM="${IPA_REALM:-EXAMPLE.TEST}"
DOMAIN="${IPA_DOMAIN:-example.test}"
ADMIN_PW="${IPA_ADMIN_PASSWORD:-Passw0rd!123}"
DM_PW="${IPA_DM_PASSWORD:-$ADMIN_PW}"
SUFFIX="dc=$(echo "$DOMAIN" | sed 's/\./,dc=/g')"
USERS_BASE="cn=users,cn=accounts,${SUFFIX}"
CRED_FILE="${CRED_FILE:-/credentials/freeipa-users.txt}"

kinit_admin() { echo "$ADMIN_PW" | kinit admin >/dev/null 2>&1; }
random_string() { head -c 24 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | cut -c "1-${1:-16}"; }
random_password() { random_string 16; }

user_exists() { kinit_admin; ipa user-show "$1" >/dev/null 2>&1; }

base_username() {
    local b
    b="$(printf '%s%s' "$(printf '%s' "$1" | cut -c1)" "$2" | tr '[:upper:]' '[:lower:]' | tr -dc 'a-z0-9')"
    [ -n "$b" ] || b="user$(random_string 6 | tr 'A-Z' 'a-z')"
    printf '%s' "$b"
}

derive_username() {
    local base candidate n
    base="$(base_username "$1" "$2")"; candidate="$base"; n=1
    while user_exists "$candidate"; do n=$((n + 1)); candidate="${base}${n}"; done
    printf '%s' "$candidate"
}

# Set a non-expiring password directly via Directory Manager (bypasses the admin-reset "must change" flag)
set_password() {
    ldapmodify -x -H ldap://localhost -D "cn=Directory Manager" -w "$DM_PW" >/dev/null <<EOF
dn: uid=$1,$USERS_BASE
changetype: modify
replace: userPassword
userPassword: $2
EOF
}

record_credentials() {
    mkdir -p "$(dirname "$CRED_FILE")"
    [ -f "$CRED_FILE" ] || printf '# FreeIPA test users (%s)\n# username | password | email | group | dn\n' "$DOMAIN" > "$CRED_FILE"
    printf '%s | %s | %s | %s | %s\n' "$1" "$2" "$3" "$4" "uid=$1,$USERS_BASE" >> "$CRED_FILE"
}

print_summary() {
    printf '\n  user created\n    username : %s\n    password : %s\n    email    : %s\n    group    : %s\n    dn       : %s\n\n' \
        "$1" "$2" "$3" "$4" "uid=$1,$USERS_BASE"
}

cmd_create() {
    local username="" password="" email="" first="" last="" group=""
    while [ $# -gt 0 ]; do
        case "$1" in
            -u|--username) username="$2"; shift 2;;
            -p|--password) password="$2"; shift 2;;
            -e|--email)    email="$2"; shift 2;;
            -f|--first)    first="$2"; shift 2;;
            -l|--last)     last="$2"; shift 2;;
            -g|--group)    group="$2"; shift 2;;
            -*)            echo "unknown option: $1" >&2; exit 2;;
            *) [ -z "$username" ] && { username="$1"; shift; } || { echo "unexpected argument: $1" >&2; exit 2; };;
        esac
    done
    kinit_admin
    [ -n "$username" ] || username="$(derive_username "$first" "$last")"
    [ -n "$first" ] || first="$username"
    [ -n "$last" ] || last="User"
    [ -n "$email" ] || email="${username}@${DOMAIN}"
    [ -n "$password" ] || password="$(random_password)"
    ipa user-add "$username" --first="$first" --last="$last" --email="$email" >/dev/null
    set_password "$username" "$password"
    [ -n "$group" ] && ipa group-add-member "$group" --users="$username" >/dev/null 2>&1 || true
    record_credentials "$username" "$password" "$email" "${group:-}"
    print_summary "$username" "$password" "$email" "${group:-none}"
}

cmd_list() {
    kinit_admin
    ipa user-find --sizelimit=0 --pkey-only 2>/dev/null | awk '/User login:/{print $3}' | while read -r u; do
        mail=$(ipa user-show "$u" 2>/dev/null | awk -F': ' '/Email address:/{print $2}')
        printf '%-16s %s\n' "$u" "${mail:-(no mail)}"
    done
}

cmd_show() { kinit_admin; ipa user-show "$1"; }

cmd_passwd() {
    local uid="$1"; local password="${2:-}"
    [ -n "$password" ] || password="$(random_password)"
    set_password "$uid" "$password"
    echo "password for $uid set to: $password"
}

cmd_update() {
    local uid="$1"; shift
    kinit_admin
    local args=()
    while [ $# -gt 0 ]; do
        case "$1" in
            -e|--email) args+=(--email="$2"); shift 2;;
            -f|--first) args+=(--first="$2"); shift 2;;
            -l|--last)  args+=(--last="$2"); shift 2;;
            *) echo "unknown option: $1" >&2; exit 2;;
        esac
    done
    [ ${#args[@]} -gt 0 ] || { echo "nothing to update" >&2; exit 2; }
    ipa user-mod "$uid" "${args[@]}" >/dev/null
    echo "updated $uid"
}

ensure_group() { kinit_admin; ipa group-show "$1" >/dev/null 2>&1 || ipa group-add "$1" --desc="$1" >/dev/null; }

cmd_seed() {
    kinit_admin
    ipa pwpolicy-mod --maxlife=3650 --minlife=0 >/dev/null 2>&1 || true
    for g in administrators employees sales support clients; do ensure_group "$g"; done

    # roster (unique to the FreeIPA directory): First Last group
    printf '%s\n' \
        "Victor Morales administrators" \
        "Isla Fisher administrators" \
        "Adrian Reyes administrators" \
        "Ruby Stone administrators" \
        "Gabriel Diaz employees" \
        "Violet Grant employees" \
        "Leo Hunter employees" \
        "Stella Fox employees" \
        "Max Palmer sales" \
        "Hazel Webb sales" \
        "Felix Dunn sales" \
        "Iris Lane sales" \
        "Oscar Boyd support" \
        "Nova Shaw support" \
        "Theo Wells support" \
        "Willow Chase support" \
        "Simon Marsh clients" \
        "Elena Frost clients" \
        "Hugo Pierce clients" \
        "Clara Bishop clients" \
    | while read -r first last group; do
        uname="$(base_username "$first" "$last")"
        user_exists "$uname" && { echo "skip existing $uname"; continue; }
        cmd_create -u "$uname" --first "$first" --last "$last" --group "$group"
    done
    echo "seed complete. credentials -> $CRED_FILE"
}

sub="${1:-}"; shift || true
case "$sub" in
    seed)   cmd_seed "$@";;
    create) cmd_create "$@";;
    list)   cmd_list "$@";;
    show)   cmd_show "$@";;
    passwd) cmd_passwd "$@";;
    update) cmd_update "$@";;
    *) echo "usage: dirctl {seed|create|list|show|passwd|update} ..." >&2; exit 2;;
esac
