#!/bin/sh
#
# /etc/local-COMMON/ldap/mkldapdb
# Copyright 2008 Jonas Smedegaard <dr@jones.dk>
#
# Setup LDAP database from skeleton files

set -e

umask 066

PRG=$(basename "$0")

TEMP=$(getopt -s sh -o b:e:d:fh -l basedn:,enable:,disable:,force,help -n "$PRG" -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"

getbasedn() {
	grep '^BASE\b' /etc/ldap/ldap.conf | sed -e 's/^BASE[[:space:]]\+//' -e 's/,[[:space:]]\+/,/g'
}
getdnsdomain() {
	dnsdomainname
}
getorgname() {
	if [ -r /etc/local-ORG/orgname ]; then
		head -n 1 /etc/local-ORG/orgname
	fi
}

# config defaults as of slapd 2.4.10-3
backend="hdb"

# extension default states (enabled/disabled)
cipux=1
horde=

# strings above, and either functions above or strings right below,
# can be overrided locally through this config file
if [ -f /etc/local/mkldapdb.cfg ]; then
	. /etc/local/mkldapdb.cfg
fi

basedn="${basedn:-$(getbasedn)}"
dnsdomain="${dnsdomain:-$(getdnsdomain)}"
orgname="${orgname:-$(getorgname)}"

showhelp() {
	cat <<EOF
Usage:   $PRG [opts...] [PHASE [PHASE...]]
Setup LDAP database from skeleton files

Options:
  -b, --basedn     LDAP Base DN (Distinguished Name) to use
                   (default: ${basedn})
  -e, --enable     Include this optional extension
  -d, --disable    Exclude this optional extension
  -t, --tempdir    Skip prep phase and use content of provided dir
  -c, --config     Include config phase
  -i, --init       Include init phase
  -f, --force      Update without asking for confirmation
  -h, --help       Show this help text

The following extensions are available:
    cipux     CipUX admin framework ${cipux:+(enabled by default)}
    horde     HORDE web-app framework ${horde:+(enabled by default)}

The following phases are possible:
    prep      Assemble slapd.conf and LDIF files with DIT parts
    config    Add/update LDAP server configuration file
    init      Purge any existing ldap data and initialize new core DIT
    main      Add general DIT for use with POSIX accounts
    mainpw    Apply/Change main admin password
    opt       Add optional DIT extensions
    optpw     Apply/Change passwords for accounts of optional extensions

When no phases are supplied, all but config and init are applied

Examples:
    $PRG
    $PRG --basedn dc=example,dc=org --enable horde prep
EOF
}

exit1()	{
	echo >&2 "Error: $1"
	echo >&2 "Exiting..."
	exit 1
}

while true ; do
	case "$1" in
		-b|--basedn) basedn="$2"; shift 2;;
		-e|--enable-extension)
			case "$2" in
				cipux|horde) eval "$2=1";;
				*) exit1 "Unknown extension \"$2\""
			esac
			shift 2
			;;
		-d|--disable-extension)
			case "$2" in
				cipux|horde) eval "$2=";;
				*) exit1 "Unknown extension \"$2\""
			esac
			shift 2
			;;
		-f|--force) force="1"; shift;;
		-h|--help) showhelp; exit 0;;
		--) shift; break;;
		*) exit1 "Internal error!";;
	esac
done

# Ensure all required values are properly resolved
for var in basedn dnsdomain orgname backend; do
	if [ -z "`eval echo '$'$var`" ]; then
		exit1 "Required variable '$var' missing. Exiting...!"
	fi
done

# concatenate files with an additional newline in between
spacecat() {
	perl -e 'foreach (@ARGV) {print "\n" if $n; $n++; open (FH, $_); print while(<FH>); close FH;}' "$@"
}

#TODO: Somehow lookup id directly instead, as getent might be slow with
#      thousands of entries, and some NSS mechanisms drop at some limit
#      i.e. openldap by default return only first 500 entries
nextfreeid() {
	type="$1"
	id="$2"
	max="$3"
	case $type in
		uid) column="3";;
		gid) column="4";;
	esac
	while getent passwd | awk -F: "{ print \$$column }" | grep -Fqx "$id"; do
		id=$(($id + 1))
		[ -z "$max" ] || [ "$id" -lt "$max" ] || return 1
	done
	echo "$id"
}

masterdir=/etc/local-COMMON/ldap
tempdir=`mktemp -dt slapd.XXXXXX`

snippets="$(run-parts --list --regex '^[0-9]+_[a-z0-9-]+\.conf\.in$' "$masterdir/slapd.conf.d")"
spacecat $snippets | sed >>"$tempdir/slapd.conf" \
		-e "s/@BACKEND@/$backend/g" \
		-e "s/@SUFFIX@/$basedn/g" \
		-e "s/@ADMIN@/cn=admin,$basedn/g"

# TODO: Better separate core from normal ldif files than "below 100"...
file=99
for section in core base cipux horde; do
	sed <"$masterdir/db/$section.ldif.in" >"$tempdir/${file}_$section.ldif" \
		-e "s/@SUFFIX@/$basedn/g" \
		-e "s/@DOMAIN@/$dnsdomain/g" \
		-e "s/@ORG@/$orgname/g"
	file=$(($file + 1))
done

# FIXME: create cipuxadm in addition to below roles!

# FIXME: fix apply passwords for roles in a sane way!
uid=10100
gid=10100
file=200
for role in admin professor assistant pupil student tutor teacher lecturer; do
	uid="$(nextfreeid uid "$uid")"
	gid="$(nextfreeid gid "$gid")"
	snippets="$masterdir/db/cipux_rolegroup.ldif.in $masterdir/db/cipux_roleuser.ldif.in"
	spacecat $snippets | sed >"$tempdir/${file}_$role.ldif" \
		-e "s/@SUFFIX@/$basedn/g" \
		-e "s/@ROLE@/$role/g" \
		-e "s/@UID@/$uid/g" \
		-e "s/@GID@/$gid/g" \
		-e "s/@DOMAIN@/$dnsdomain/g" \
		-e "s/@ORG@/$orgname/g"
	uid=$(($uid + 1))
	gid=$(($gid + 1))
	file=$(($file + 1))
done

file=300
for db in passwd group; do
	getent $db >"$tempdir/$db.dump"
	( cd /usr/share/migrationtools && ./migrate_$db.pl "$tempdir/$db.dump" >"$tempdir/${file}_$db.ldif" )
	file=$(($file + 1))
done

# FIXME: Set core password using slappasswd or similar (no cleartext password!)
#invoke-rc.d slapd stop
#slapadd -l "$tempdir/99_core.ldif"
#invoke-rc.d slapd start
#ldappasswd -x -h localhost -D "cn=admin,$basedn" -S -w supersecretpassword "cn=admin,$basedn"
for file in $(run-parts --list --regex '^1[0-9]{2}_[a-z0-9-]+\.ldif' "$tempdir"); do
	ldapadd -x -h localhost -D "cn=admin,$basedn" -f "$file" -W
done
for role in cipux horde; do
	echo "Securing $role..."
	ldappasswd -x -h localhost -D "cn=admin,$basedn" -S -W "cn=$role,ou=Entities,ou=Access Control,$basedn"
done

# FIXME: Write addmember(), that create group as needed
#ldapmodify -x -h localhost -D "cn=admin,$basedn" -W <<EOF
#dn: cn=DSA,ou=Administrators,ou=Groups,ou=Access Control,$basedn
#changetype: modify
#add: uniqueMember
#uniqueMember: cn=cipux,ou=Entities,ou=Access Control,$basedn
#EOF
ldapadd -x -h localhost -D "cn=admin,$basedn" -W <<EOF
dn: cn=DSA,ou=Administrators,ou=Groups,ou=Access Control,$basedn
objectClass: groupOfUniqueNames
cn: DSA
description: Directory System Agent administrators
uniqueMember: cn=cipux,ou=Entities,ou=Access Control,$basedn
EOF
ldapadd -x -h localhost -D "cn=admin,$basedn" -W <<EOF
dn: cn=SAM,ou=Administrators,ou=Groups,ou=Access Control,$basedn
objectClass: groupOfUniqueNames
cn: SAM
description: Samba and NSS services administrators
uniqueMember: cn=horde,ou=Entities,ou=Access Control,$basedn
EOF

# TODO: Add "uid=cifsdc,ou=Entities,ou=Access Control,@SUFFIX@" to group
#       "cn=SAM,ou=Administrators,ou=Access Control,@SUFFIX@" for samba

for file in $(run-parts --list --regex '^2[0-9]{2}_[a-z0-9-]+\.ldif' "$tempdir"); do
	ldapadd -x -h localhost -D "cn=admin,$basedn" -f "$file" -W
done

# FIXME: Check (and maybe correct) basedn from migrationtools-generated ldifs
#for file in $(run-parts --list --regex '^3[0-9]{2}_[a-z0-9-]+\.ldif' "$tempdir"); do
#	ldapadd -x -h localhost -D "cn=admin,$basedn" -f "$file" -W
#done