#!/usr/bin/bash

set -f
PROGNAME=${0##*/}

declare -A TYPES=(
    ['writer']='zlc-role-writer'
    ['dashboard-reader']='zlc-role-dashboard-reader'
    ['superuser']='superuser'
)

declare -A ROLES=(
    ['zlc-role-writer']='
{
  "cluster": [ "monitor" ],
  "indices": [
    {
      "names": [ "logs-*", "metrics-*" ],
      "privileges": [ "auto_configure", "create" ]
    }
  ]
}'
    ['zlc-role-dashboard-reader']='
{
  "cluster": [],
  "indices": [
    {
      "names": [ "/logs-~(elasticsearch)-zlc/" ],
      "privileges": [ "read", "view_index_metadata" ]
    }
  ],
  "applications": [
    {
      "application": "kibana-.kibana",
      "privileges": [ "feature_dashboard.read" ],
      "resources": [ "space:dashboard" ]
    }
  ]
}'
)

function _log() { echo "$STDOUT_LABEL: $*" >&2; }
function info() { STDOUT_LABEL=INFO _log "$@"; }
function fatal() { STDOUT_LABEL=FATAL _log "$@"; exit 2; }

function exit_usage() {
    local status=${1:-0}
    [[ $status != 0 ]] && exec >&2
    echo "\
Usage: es-user-create -n NAME [-t TYPE] [-r ROLE] [OPTION...]
       es-role-create -t TYPE | -r ROLE [OPTION...]
Script to create Elasticsearch roles and users

Available options:
    -n, --name      User name to create
    -t, --type      Compute roles based on predefined type
    -r, --role      Roles to create and/or assign, comma separated list
    -p, --password  Password for the user instead of random
    -P, --showpass  Print password on stdout if it was generated
    --recreate-role Delete the role if it exists
    --recreate-user Delete the user if it exists
    -h, --help      Display this help

Supported types:"
    echo "${!TYPES[*]}" |sort -u |xargs |fold -s -w 50 |
        sed -e 's! !, !g' -e 's,^,    ,'
    exit "$status"
}

NAME=
ROLE=()
TYPE=
RECREATE_ROLE=
RECREATE_USER=
while (( $# > 0 )); do
    case "$1" in
        -n|--name) NAME=$2; shift ;;
        -p|--pass) PASS=$2; shift ;;
        -r|--role) ROLE+=( ${2//,/ } ); shift ;;
        -t|--type) TYPE=$2; shift ;;
        --recreate-role) RECREATE_ROLE=1 ;;
        --recreate-user) RECREATE_USER=1 ;;
        -P|--showpass) SHOWPASS=1 ;;
        -h|--help) exit_usage 0 ;;
        *) exit_usage 1 ;;
    esac
    shift
done

if [[ -n $TYPE ]]; then
    [[ -z ${TYPES[$TYPE]} ]] && exit_usage 1
    ROLE+=( ${TYPES[$TYPE]//,/ } )
fi
[[ -z $ROLE ]] && exit_usage 1
[[ $PROGNAME == *user* && -z $NAME ]] && exit_usage 1

for r in "${ROLE[@]}"; do
    [[ -z $r ]] && continue
    create_role=

    # disable both curl --fail-with-body (el9) and -f (el8)
    out=$(ES_LOCAL_CURL_FAIL_WITH_BODY= \
          ES_LOCAL_CURL_FAIL= \
        es-curl "/_security/role/$r" -o /dev/null -w '%{http_code}')
    if [[ $out == 200 ]]; then
        info "Role already exists: $r"
        if [[ -n $RECREATE_ROLE ]]; then
            [[ -z ${ROLES[$r]} ]] && fatal "No definition to recreate role, cannot delete: $r"
            info "Delete existing role: $r"
            es-curl "/_security/role/$r" -X DELETE >/dev/null
            (( $? == 0 )) || fatal "Failed to delete existing role: $r"
            create_role=1
        fi
    elif [[ $out == 404 ]]; then
        create_role=1
    else
        fatal "Failed to check role: $r"
    fi
    if [[ -n $create_role ]]; then
        [[ -z ${ROLES[$r]} ]] && fatal "Unsupported role: $r"
        info "Create role: $r"
        echo "${ROLES[$r]}" |es-curl "/_security/role/$r" -X POST -d @- >/dev/null
        (( $? == 0 )) || fatal "Role did not exist and creation failed: $r"
    fi
done

[[ -z $NAME ]] && exit 0

if [[ -n $PASS ]]; then
    [[ $PASS == env:* ]] && { v=${PASS#env:}; PASS=${!v}; export -n "$v"; }
    [[ ${#PASS} -lt 6 ]] && fatal 'Given password too short (min 6)'
    SHOWPASS=
fi

create_user=

# disable both curl --fail-with-body (el9) and -f (el8)
out=$(ES_LOCAL_CURL_FAIL_WITH_BODY= \
      ES_LOCAL_CURL_FAIL= \
    es-curl "/_security/user/$NAME" -o /dev/null -w '%{http_code}')
if [[ $out == 200 ]]; then
    info "User already exists: $NAME"
    if [[ -n $RECREATE_USER ]]; then
        info "Delete existing user: $NAME"
        es-curl "/_security/user/$NAME" -X DELETE >/dev/null
        (( $? == 0 )) || fatal "Failed to delete existing user: $NAME"
        create_user=1
    fi
elif [[ $out == 404 ]]; then
    create_user=1
else
    fatal "Failed to check user: $NAME"
fi

[[ -z $create_user ]] && exit 0
[[ -z $PASS ]] && PASS=$(LC_ALL=C tr -dc 'A-Za-z0-9_@./:=,-' </dev/urandom | head -c 20)
info "Create user: $NAME"
pass=$PASS jq -n --arg r "${ROLE[*]}" '{ password: $ENV.pass, roles: ($r|split(" ")) }' |
    es-curl "/_security/user/$NAME" -X POST -d @- >/dev/null
(( $? == 0 )) || fatal "Failed to create user: $NAME"
[[ -n $SHOWPASS ]] && echo "$PASS"
exit 0
