#!/bin/sh

name="${__target_host}"
manager_dn=$(cat "${__object}/parameter/manager-dn")
manager_password_hash=$(cat "${__object}/parameter/manager-password-hash")
serverid=$(cat "${__object}/parameter/serverid")
suffix=$(cat "${__object}/parameter/suffix")
slapd_modules=$(cat "${__object}/parameter/module" 2>/dev/null || true)
schemas=$(cat "${__object}/parameter/schema")
slapd_urls=$(tr '\n' ' ' < "${__object}/parameter/slapd-url")
tls_cipher_suite=$(cat "${__object}/parameter/tls-cipher-suite" 2>/dev/null || true)
extra_config=$(cat "${__object}/parameter/extra-config" || true)


os="$(cat "${__global}/explorer/os")"

# Setup OS-dependent vars
CONF_OWNER="root"
CONF_GROUP="root"
case "${os}" in
    freebsd)
        PKGS="openldap-server"
        ETC="/usr/local/etc"
        SLAPD_DIR="/usr/local/etc/openldap"
        SLAPD_DATA_DIR="/var/db/openldap-data"
        SLAPD_RUN_DIR="/var/run/openldap"
        SLAPD_MODULE_PATH="/usr/local/libexec/openldap"
        SLAPD_MODULE_TYPE="la"
        if [ -z "${slapd_modules}" ]; then
            # It looks like ppolicy and syncprov must be compiled
            slapd_modules="back_mdb back_monitor"
        fi
        CONF_OWNER="ldap"
        CONF_GROUP="ldap"
        if [ -z "${tls_cipher_suite}" ]; then
            # TODO: research default for FreeBSD. 'NORMAL' appears to not work
            tls_cipher_suite="HIGH:MEDIUM:+SSLv2"
        fi
        ;;
    debian|ubuntu|devuan)
        PKGS="slapd ldap-utils"
        ETC="/etc"
        SLAPD_DIR="/etc/ldap"
        SLAPD_DATA_DIR="/var/lib/ldap"
        SLAPD_RUN_DIR="/var/run/slapd"
        SLAPD_MODULE_PATH="/usr/lib/ldap"
        SLAPD_MODULE_TYPE="la"
        if [ -z "${slapd_modules}" ]; then
            slapd_modules="back_mdb ppolicy syncprov back_monitor"
        fi
        CONF_OWNER="openldap"
        CONF_GROUP="openldap"
        if [ -z "${tls_cipher_suite}" ]; then
            tls_cipher_suite="NORMAL"
        fi
        ;;
    alpine)
        PKGS="openldap openldap-clients"
        ETC="/etc"
        SLAPD_DIR="/etc/openldap"
        SLAPD_DATA_DIR="/var/lib/openldap"
        SLAPD_RUN_DIR="/var/run/openldap"
        SLAPD_MODULE_PATH="/usr/lib/openldap"
        SLAPD_MODULE_TYPE="so"
        if [ -z "${slapd_modules}" ]; then
            slapd_modules="back_mdb ppolicy syncprov back_monitor"
            PKGS="$PKGS openldap-back-mdb openldap-back-monitor openldap-overlay-all"
        fi
        CONF_OWNER="ldap"
        CONF_GROUP="$SLAPD_USER"
        if [ -z "${tls_cipher_suite}" ]; then
            tls_cipher_suite="DEFAULT"
        fi
        ;;
    *)
        echo "Don't know the openldap defaults for: $os" >&2
        exit 1
        ;;
esac

PKG_MAIN=$(echo "${PKGS}" | awk '{print $1;}')


# Determine if __letsencrypt_cert is to be used and setup vars accordingly
if [ -f "${__object}/parameter/tls-cert" ]; then
    tls_cert=$(cat "${__object}/parameter/tls-cert")

    if [ ! -f "${__object}/parameter/tls-privkey" ]; then
        echo "When tls-cert is defined, tls-privkey is also required." >&2
        exit 1
    fi
    tls_privkey=$(cat "${__object}/parameter/tls-privkey")

    if [ ! -f "${__object}/parameter/tls-ca" ]; then
        echo "When tls-cert is defined, tls-ca is also required." >&2
        exit 1
    fi
    tls_ca=$(cat "${__object}/parameter/tls-ca")

    _skip_letsencrypt_cert="YES"
else
    if [ ! -f "${__object}/parameter/admin-email" ]; then
        echo "When using __letsencrypt_cert, admin-email is also required." >&2
        exit 1
    fi
    admin_email=$(cat "${__object}/parameter/admin-email")

    tls_cert="${SLAPD_DIR}/sasl2/cert.pem"
    tls_privkey="${SLAPD_DIR}/sasl2/privkey.pem"
    tls_ca="${SLAPD_DIR}/sasl2/chain.pem"
fi

mkdir "${__object}/files"
ldapconf="${__object}/files/ldapconf"

replication=""
if [ -f "${__object}/parameter/replicate" ]; then
    replication=yes

    if [ ! -f "${__object}/parameter/syncrepl-searchbase" ]; then
        echo "Requiring the searchbase for replication" >&2
        exit 1
    fi
    syncrepl_searchbase=$(cat "${__object}/parameter/syncrepl-searchbase")

    if [ ! -f "${__object}/parameter/syncrepl-credentials" ]; then
        echo "Requiring credentials for replication" >&2
        exit 1
    fi

    syncrepl_credentials=$(cat "${__object}/parameter/syncrepl-credentials")

    if [ ! -f "${__object}/parameter/syncrepl-host" ]; then
        echo "Requiring host(s) for replication" >&2
        exit 1
    fi
    syncrepl_hosts=$(cat "${__object}/parameter/syncrepl-host")

fi

# Install required packages
for pkg in ${PKGS}; do
    __package "${pkg}"
done


require="__package/${PKG_MAIN}" __start_on_boot slapd

# Setup -h flag for the listeners. See man slapd (-h flag).
case "${os}" in
    freebsd)
        require="__start_on_boot/slapd" __key_value \
               --file "/etc/rc.conf" \
               --key "slapd_flags" \
               --value "\"-h '${slapd_urls}'\"" \
               --delimiter "=" \
               --comment "# LDAP Listener URLs" \
               "${__target_host}__slapd_flags"
        ;;
    debian|ubuntu|devuan)
        require="__package/${PKG_MAIN}" __line rm_slapd_conf \
               --file ${ETC}/default/slapd \
               --regex 'SLAPD_CONF=.*' \
               --state absent

        require="__package/${PKG_MAIN}" __line rm_slapd_services \
               --file ${ETC}/default/slapd \
               --regex 'SLAPD_SERVICES=.*' \
               --state absent

        require="__line/rm_slapd_conf" __line add_slapd_conf \
               --file ${ETC}/default/slapd \
               --line "SLAPD_CONF=${SLAPD_DIR}/slapd.conf" \
               --state present

        require="__line/rm_slapd_services" __line add_slapd_services \
               --file ${ETC}/default/slapd \
               --line "SLAPD_SERVICES=\"${slapd_urls}\"" \
               --state present
        ;;
    alpine)
        require="__package/${PKG_MAIN}" __line add_slapd_services \
               --file ${ETC}/conf.d/slapd \
               --line "command_args=\"-h '${slapd_urls}'\"" \
               --state present
        ;;
    *)
        # Nothing to do here, move on.
        ;;
esac


if [ -z "${_skip_letsencrypt_cert}" ]; then
    if [ -f "${__object}/parameter/staging" ]; then
        staging="--staging"
    else
        staging=""
    fi

    # shellcheck disable=SC2086
    __directory ${SLAPD_DIR}/sasl2
    require="__directory/${SLAPD_DIR}/sasl2" __letsencrypt_cert "${name}" \
        --admin-email "${admin_email}" \
        --renew-hook "cp ${ETC}/letsencrypt/live/${name}/*.pem ${SLAPD_DIR}/sasl2 && chown -R ${CONF_OWNER}:${CONF_GROUP} ${SLAPD_DIR}/sasl2 && service slapd restart" \
        --automatic-renewal "${staging}"
fi

require="__package/${PKG_MAIN}" __directory ${SLAPD_DIR}/slapd.d --state absent

if [ -z "${_skip_letsencrypt_cert}" ]; then
    require="__package/${PKG_MAIN} __letsencrypt_cert/${name}" \
           __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \
           --source "${ldapconf}"
else
    require="__package/${PKG_MAIN}" \
           __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \
           --source "${ldapconf}"
fi

# Start slapd.conf
cat << EOF > "${ldapconf}"
pidfile ${SLAPD_RUN_DIR}/slapd.pid
argsfile ${SLAPD_RUN_DIR}/slapd.args

TLSCipherSuite ${tls_cipher_suite}
TLSCertificateFile ${tls_cert}
TLSCertificateKeyFile ${tls_privkey}
TLSCACertificateFile ${tls_ca}

disallow bind_anon
require bind
security tls=1
EOF

# Add specified schemas
for schema in ${schemas}; do
    echo "include ${SLAPD_DIR}/schema/${schema}.schema" >> "${ldapconf}"
done

# Add specified modules
echo "modulepath ${SLAPD_MODULE_PATH}" >> "${ldapconf}"
for module in ${slapd_modules}; do
    echo "moduleload ${module}.${SLAPD_MODULE_TYPE}" >> "${ldapconf}"
done

# Rest of the config
cat << EOF >> "${ldapconf}"
loglevel 1024

database mdb
maxsize 1073741824

suffix "${suffix}"
directory ${SLAPD_DATA_DIR}
rootdn "${manager_dn}"
rootpw "${manager_password_hash}"

index objectClass eq,pres
index ou,cn,mail,surname,givenname eq,pres,sub
index uidNumber,gidNumber,loginShell eq,pres
index uid,memberUid eq,pres,sub
index nisMapName,nisMapEntry eq,pres,sub
index entryCSN,entryUUID eq

${extra_config}

serverid ${serverid}
EOF

# Setup replication
if [ "${replication}" ]; then
    rid=1;
    for syncrepl in ${syncrepl_hosts}; do
    cat <<EOF >> "${ldapconf}"
syncrepl rid=${rid}
 provider=ldap://${syncrepl}
 bindmethod=simple
 starttls=yes
 binddn="${manager_dn}"
 credentials=${syncrepl_credentials}
 searchbase="${syncrepl_searchbase}"
 type=refreshAndPersist
 retry="5 + 5 +"
 interval=00:00:00:05
EOF
    rid=$((rid + 1))
    done
    cat <<EOF >> "${ldapconf}"
mirrormode true
overlay syncprov
syncprov-checkpoint 100 5
syncprov-sessionlog 100

database monitor
limits dn.exact="${manager_dn}" time=unlimited size=unlimited
EOF
fi
