Files

168 lines
6.1 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# Seed the CI domain: the OU tree, the service account, the PS-* groups and the test users.
# Runs once from entrypoint.sh, before samba starts, against the local sam.ldb.
# Safe to rerun: "already exists" is ignored and every user gets a new random password.
# Never prints a password. Writes $PRIVATE/users.json (0600); the entrypoint exports it.
set -euo pipefail
BASE="DC=pstest,DC=local"
PRIVATE=/var/lib/samba/private
SAM=$PRIVATE/sam.ldb
GROUPS_FILE=/usr/local/share/pstest/groups.txt
BIND_SECRET=/run/secrets/ldap_bind_password
PS_OU="OU=PacketStation,OU=Groups,OU=PESCO_FARMINGTON"
USERS_OU="OU=Users,OU=PESCO_FARMINGTON"
MISSING_GROUP="PS-14-Electrical-Lead" # left out on purpose: the reconcile report must flag it
EXTRA_GROUP="PS-Extra-Legacy" # added on purpose: the reconcile report must list it
NESTED_GROUP="Shop-Welders-Nested"
log() { echo "samba-dc seed: $*"; }
# Run a command. Ignore "already exists". On any other failure show its output and stop.
# Callers never pass a password in a way that ends up in this output.
try() {
local out
if out=$("$@" 2>&1); then
return 0
fi
if printf '%s' "$out" | grep -qiE 'already exists|ENTRY_ALREADY_EXISTS|already a member'; then
return 0
fi
log "failed: $1 $2 $3" >&2
printf '%s\n' "$out" | sed 's/^/ /' >&2
exit 1
}
add_ou() {
local dn="$1,$BASE"
local ldif
ldif=$(mktemp)
printf 'dn: %s\nobjectClass: top\nobjectClass: organizationalUnit\n' "$dn" > "$ldif"
try ldbadd -H "$SAM" "$ldif"
rm -f "$ldif"
}
random_password() {
# 24 random base64 characters plus a fixed tail with upper, lower, digit and symbol.
printf '%s-Pa9' "$(openssl rand -base64 18 | tr -d '\n')"
}
set_password() {
# $1 sam, $2 password. Not logged. Visible only inside this container's process list.
try samba-tool user setpassword "$1" --newpassword="$2"
}
set_attrs() {
# $1 dn, $2 display name, $3 employeeID or empty.
local ldif
ldif=$(mktemp)
{
printf 'dn: %s\nchangetype: modify\n' "$1"
printf 'replace: displayName\ndisplayName: %s\n-\n' "$2"
if [ -n "$3" ]; then
printf 'replace: employeeID\nemployeeID: %s\n-\n' "$3"
fi
} > "$ldif"
try ldbmodify -H "$SAM" "$ldif"
rm -f "$ldif"
}
# ---- OU tree, mirroring PESCO ----
log "OU tree"
add_ou "OU=PESCO_FARMINGTON"
add_ou "OU=Groups,OU=PESCO_FARMINGTON"
add_ou "$PS_OU"
add_ou "$USERS_OU"
add_ou "OU=Admin"
add_ou "OU=System_Accounts,OU=Admin"
# ---- Service account (password = the app's ldap_bind_password secret) ----
log "service account svc-packetstation"
if [ ! -s "$BIND_SECRET" ]; then
log "missing or empty secret file $BIND_SECRET" >&2
exit 1
fi
try samba-tool user create svc-packetstation --random-password --use-username-as-cn \
--userou="OU=System_Accounts,OU=Admin" --description="Packet Station service account (CI)"
set_password svc-packetstation "$(head -n 1 "$BIND_SECRET" | tr -d '\r\n')"
try samba-tool user setexpiry svc-packetstation --noexpiry
# ---- Groups ----
log "PacketStation groups"
count=0
while IFS= read -r name || [ -n "$name" ]; do
name=$(printf '%s' "$name" | tr -d '\r' | sed 's/[[:space:]]*$//')
[ -z "$name" ] && continue
[ "$name" = "$MISSING_GROUP" ] && continue
try samba-tool group add "$name" --groupou="$PS_OU" \
--group-scope=Global --group-type=Security --description="Packet Station: $name"
count=$((count + 1))
done < "$GROUPS_FILE"
try samba-tool group add "$EXTRA_GROUP" --groupou="$PS_OU" \
--group-scope=Global --group-type=Security --description="Packet Station: $EXTRA_GROUP"
try samba-tool group add "$NESTED_GROUP" --groupou="OU=Groups,OU=PESCO_FARMINGTON" \
--group-scope=Global --group-type=Security --description="Helper group for the nested membership test"
try samba-tool group addmembers PS-10-WeldAssembly-Worker "$NESTED_GROUP"
log "created $count mapped groups (without $MISSING_GROUP), plus $EXTRA_GROUP and $NESTED_GROUP"
# ---- Users: sam|given|surname|employeeID|groups (comma separated) ----
ROSTER='admin.ann|Ann|Admin|1001|PS-Admins
clerk.clara|Clara|Clerk|4001|PS-Clerical
clerk.leo|Leo|Lead|4002|PS-Clerical-Supervisor
wh.wade|Wade|Warehouse|6001|PS-Warehouse
qc.quinn|Quinn|QC|5001|PS-QC
pm.paula|Paula|PM|7001|PS-ProjectMgmt
weld.andy|Andy|Welder|3003|PS-03-WeldParts-Worker
lead.lou|Lou|Lead|3002|PS-03-WeldParts-Lead
sup.wes|Wes|Super|2003|PS-03-WeldParts-Supervisor
multi.mia|Mia|Multi|2006|PS-Clerical,PS-06-CodeBldg2-Supervisor
nested.ned|Ned|Nested|3010|Shop-Welders-Nested
both.bo|Bo|Both|3011|PS-03-WeldParts-Worker,PS-03-WeldParts-Supervisor
disabled.dan|Dan|Disabled|3004|PS-03-WeldParts-Worker
noemp.nora|Nora|NoEmp||PS-Warehouse
dup.dave|Dave|Dup|9001|PS-03-WeldParts-Worker
dup.dina|Dina|Dup|9001|PS-Warehouse
norole.nick|Nick|NoRole|8001|'
umask 077
rows=$(mktemp) # sam, password, emp, display; tab separated; 0600; removed below
log "users"
while IFS='|' read -r sam given surname emp groups; do
[ -z "$sam" ] && continue
display="$given $surname"
dn="CN=$sam,$USERS_OU,$BASE"
try samba-tool user create "$sam" --random-password --use-username-as-cn \
--userou="$USERS_OU" --given-name="$given" --surname="$surname" \
--mail-address="$sam@pstest.local"
pw=$(random_password)
set_password "$sam" "$pw"
set_attrs "$dn" "$display" "$emp"
if [ -n "$groups" ]; then
IFS=',' read -r -a list <<< "$groups"
for g in "${list[@]}"; do
try samba-tool group addmembers "$g" "$sam"
done
fi
printf '%s\t%s\t%s\t%s\n' "$sam" "$pw" "$emp" "$display" >> "$rows"
unset pw
log " $sam"
done <<< "$ROSTER"
try samba-tool user disable disabled.dan
# ---- users.json, the format in the phase 1 contract ----
python3 - "$rows" "$PRIVATE/users.json" <<'PY'
import json, sys
users = {}
with open(sys.argv[1], encoding="utf-8") as f:
for line in f:
sam, password, emp, display = line.rstrip("\n").split("\t")
users[sam] = {"password": password, "emp_no": emp or None, "display_name": display}
with open(sys.argv[2], "w", encoding="utf-8") as f:
json.dump({"domain": "pstest.local", "users": users}, f, indent=2, sort_keys=True)
f.write("\n")
PY
rm -f "$rows"
chmod 0600 "$PRIVATE/users.json"
log "done: $(grep -c '"password"' "$PRIVATE/users.json") users written to users.json"