#!/bin/sh
# Smoke test of the LDAP stack: run ldap-rest against a throw-away slapd and
# check that reading, searching and creating a user really go through.
set -e

# slapd and slapadd live in /usr/sbin, not in PATH for the unprivileged user
# autopkgtest runs tests as
PATH="/usr/sbin:/sbin:$PATH"
export PATH

TMP="${AUTOPKGTEST_TMP:-/tmp}"
BASE=dc=example,dc=com

# Let the kernel pick the ports: fixed ones would collide with whatever else
# runs on the CI machine
free_port() {
	node -e 'const probe = require("net").createServer();
probe.listen(0, "127.0.0.1", () => {
  const { port } = probe.address();
  probe.close(() => console.log(port));
});'
}

slapd_pid=
server=
trap 'kill $slapd_pid $server 2>/dev/null || true' EXIT

# --- a throw-away directory holding a single user ---
mkdir -p "$TMP/slapd.d" "$TMP/db"
cat > "$TMP/config.ldif" <<EOF
dn: cn=config
objectClass: olcGlobal
cn: config

dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib/ldap
olcModuleLoad: back_mdb

dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema

include: file:///etc/ldap/schema/core.ldif
include: file:///etc/ldap/schema/cosine.ldif
include: file:///etc/ldap/schema/inetorgperson.ldif

dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: $TMP/db
olcSuffix: $BASE
olcRootDN: cn=admin,$BASE
olcRootPW: secret
olcAccess: to * by * manage
EOF
slapadd -F "$TMP/slapd.d" -n 0 -l "$TMP/config.ldif"

cat > "$TMP/base.ldif" <<EOF
dn: $BASE
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example
dc: example

dn: ou=users,$BASE
objectClass: organizationalUnit
ou: users

dn: uid=jsmith,ou=users,$BASE
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: jsmith
cn: John Smith
sn: Smith
mail: jsmith@example.com
EOF
slapadd -F "$TMP/slapd.d" -n 1 -l "$TMP/base.ldif"

ldap_port=$(free_port)
ldap_url="ldap://127.0.0.1:$ldap_port"
slapd -h "$ldap_url/" -F "$TMP/slapd.d" -d 0 >"$TMP/slapd.log" 2>&1 &
slapd_pid=$!

i=0
until ldapsearch -x -H "$ldap_url" -b "$BASE" -s base >/dev/null 2>&1; do
	i=$((i + 1))
	if [ $i -gt 30 ] || ! kill -0 $slapd_pid 2>/dev/null; then
		echo "slapd did not answer on $ldap_url" >&2
		cat "$TMP/slapd.log" >&2
		exit 1
	fi
	sleep 1
done
echo "OK: slapd listening on $ldap_url"

# --- ldap-rest on top of it ---
port=$(free_port)
url="http://127.0.0.1:$port/api/v1/ldap/users"
ldap-rest \
	--plugin core/ldap/flatGeneric \
	--ldap-url "$ldap_url" \
	--ldap-dn "cn=admin,$BASE" \
	--ldap-pwd secret \
	--ldap-base "$BASE" \
	--ldap-flat-schema /usr/share/ldap-rest/static/schemas/standard/users.json \
	--port "$port" >"$TMP/ldap-rest.log" 2>&1 &
server=$!

i=0
until curl -sf "$url/jsmith" -o /dev/null; do
	i=$((i + 1))
	if [ $i -gt 30 ] || ! kill -0 $server 2>/dev/null; then
		echo "ldap-rest did not answer on port $port" >&2
		cat "$TMP/ldap-rest.log" >&2
		exit 1
	fi
	sleep 1
done
echo "OK: ldap-rest listening on port $port"

# get EXPECTED-STATUS PATH: body left in $TMP/body
get() {
	code=$(curl -s -o "$TMP/body" -w '%{http_code}' "$url$2")
	if [ "$code" != "$1" ]; then
		echo "GET $url$2: got $code, expected $1" >&2
		cat "$TMP/body" >&2
		exit 1
	fi
}

# The seeded user is only in the directory: reading it proves the whole chain
get 200 /jsmith
grep -q jsmith@example.com "$TMP/body"
echo "OK: read uid=jsmith through the API"

get 200 '?match=uid=j*'
grep -q '"jsmith"' "$TMP/body"
echo "OK: search returned uid=jsmith"

code=$(curl -s -o "$TMP/body" -w '%{http_code}' -X POST \
	-H 'Content-Type: application/json' \
	-d '{"uid":"adoe","cn":"Alice Doe","sn":"Doe","mail":"adoe@example.com"}' \
	"$url")
test "$code" = 201 || {
	echo "POST $url: got $code, expected 201" >&2
	cat "$TMP/body" >&2
	exit 1
}
ldapsearch -x -LLL -H "$ldap_url" -b "$BASE" '(uid=adoe)' dn | grep -q '^dn: uid=adoe,'
echo "OK: created uid=adoe, entry present in the directory"

echo "All checks passed"
