#!/bin/bash
# net/l4 - LDAP helper

set -e

source "/home/zwelch/src/mcf/mcsh-release/install/share/mcsh/mcsh.sh"

lib_load 'net/ldap'


######
# Script Configuration

l4_config_files() {
	case "$1" in
	(tool|usertool) echo "$script_name.passwd" ;;
	esac
}


######
# Low-level functions

client_gen_config() {
	cat <<EOF
[krb5ldap]
nss_passwd=passwd: files ldap
nss_group=group: files ldap
nss_shadow=shadow: files ldap
nss_netgroup=netgroup: files ldap
pam_auth=auth       sufficient   pam_krb5.so
         auth       required     pam_unix.so nullok_secure use_first_pass
pam_account=account    sufficient   pam_krb5.so
            account    required     pam_unix.so
pam_password=password   sufficient   pam_krb5.so
             password   required     pam_unix.so nullok obscure min=4 max=8 md5
pam_session=session    required     pam_unix.so
            session    required     pam_mkhomedir.so skel=/etc/skel/
            session    optional     pam_krb5.so
            session    optional     pam_foreground.so

[krb5ldap.cached]
nss_passwd=passwd: files ldap [NOTFOUND=return] db
nss_group=group: files ldap [NOTFOUND=return] db
nss_shadow=shadow: files ldap
nss_netgroup=netgroup: files ldap
pam_auth=auth   required       pam_env.so
         auth   sufficient     pam_unix.so likeauth nullok
         auth   [default=ignore success=1 service_err=reset] pam_krb5.so use_first_pass
         auth   [default=die success=done] pam_ccreds.so action=validate use_first_pass
         auth   sufficient pam_ccreds.so action=store use_first_pass
         auth   required        pam_deny.so
pam_account=account    sufficient   pam_krb5.so
            account    required     pam_unix.so
pam_password=password   sufficient   pam_krb5.so
             password   required     pam_unix.so nullok obscure min=4 max=8 md5
pam_session=session    required     pam_unix.so
            session    required     pam_mkhomedir.so skel=/etc/skel/
            session    optional     pam_krb5.so
            session    optional     pam_foreground.so
EOF
}

l4_remote() { cmd_dispatch "$@"; }
l4_remote_usage() {
	cat <<USAGE
<cmd>
Remote Commands:
	install <host>			Installs LDAP packages on remote host
	config <host>			Deploys LDAP support on remote host
USAGE
}

l4_remote_install() {
	has_args 1 "$@"
	apt_install_remote "$1" "${ldap_client_packages[@]}"
}

l4_remote_config() {
	has_args 1 "$@"
	local host=$1

	local src
	src=$(cmd_tempfile)
	client_gen_config >$src

	local auth_config_file
	auth_config_file=$etcdir/auth-client-config/profile.d/krb-auth-config
	remote_deploy_file $host "$src" "$auth_config_file"

	run rm -f $src
	remote_sudo $host chown root:root $auth_config_file
	remote_sudo $host chmod go-rwx $auth_config_file
	remote_sudo $host auth-client-config -a -p krb5ldap
}

setup_schema() {
	cat <<EOF
dn: $ldap_hosts_dn
objectClass: organizationalUnit
ou: $ldap_hosts_ou

dn: $ldap_users_dn
objectClass: organizationalUnit
ou: $ldap_users_ou

dn: $ldap_groups_dn
objectClass: organizationalUnit
ou: $ldap_groups_ou
EOF
}

setup_sasl() {
	cat <<EOF
olcSaslHost: $kdc_host.$domain
olcSaslRealm: $ldap_realm
olcSaslSecProps: noplain,noactive,noanonymous,minssf=56
olcAuthzRegexp: {0}"uid=([^/]*),cn=$domain,cn=GSSAPI,cn=auth" "uid=\$1,$ldap_users_dn"
olcAuthzRegexp: {1}"uid=host/([^/]*).$domain,cn=$domain,cn=gssapi,cn=auth" "cn=\$1,$ldap_hosts_dn"
olcAuthzRegexp: {2}"uid=ldap/admin,cn=$domain,cn=gssapi,cn=auth" "cn=admin,cn=config"
EOF
}


######
# Host CLI (incomplete)

l4_host() { cmd_dispatch "$@"; }
l4_host_exists() { entity_exists host "$@"; }
l4_host_extract() { entity_extract host "$@"; }
l4_host_id() { entity_extract host "$1" hid?; }
l4_host_ids() { entity_ids host hid? "$@"; }
l4_host_next_id() { entity_next_id host; }
l4_host_delete() { entity_delete host "$@"; }

l4_host_dn() { cmd_dispatch "$@"; }
l4_host_dn_usage() { entity_dn_usage; }
l4_host_dn_add() { entity_dn_modify host cn add "$@"; }
l4_host_dn_edit() { entity_dn_modify host cn modify "$@"; }
l4_host_dn_delete() { entity_dn_modify host cn delete "$@"; }

l4_host_attr() { cmd_dispatch "$@"; }
l4_host_attr_usage() { entity_attr_usage hid?; }
l4_host_attr_add() { entity_attr_modify host modify add "$@"; }
l4_host_attr_edit() { entity_attr_modify host modify replace "$@"; }
l4_host_attr_delete() { entity_attr_modify host modify delete "$@"; }


######
# Group CLI

l4_group() { cmd_dispatch "$@"; }
l4_group_usage() {
	cat <<EOF
<cmd> [...]
Group Commands:
	info [<name>+]			Prints human readable list
	list				Prints list of group names
	ids				Prints list of group ids
	add <name> <gid> <desc>		Adds a new group
	delete <name>+			Deletes one or more groups

Group Query Commands:
	next_id				Prints next available group ID
	exists <name>			Returns true if group exists
	show <name> [<attrs>+]		Displays group record
	id <name>			Displays group id

Group Command Groups:
	dn ....				Group of DN commands
	attr ....			Group of attribute commands
	user ....			Group of member user commands
EOF
}

l4_group_exists() { entity_exists group "$@"; }
l4_group_extract() { entity_extract group "$@"; }
l4_group_id() { entity_extract group "$1" gidNumber; }
l4_group_ids() { entity_ids group gidNumber "$@"; }
l4_group_next_id() { entity_next_id group; }
l4_group_delete() { entity_delete group "$@"; }

l4_group_dn() { cmd_dispatch "$@"; }
l4_group_dn_usage() { entity_dn_usage; }
l4_group_dn_add() { entity_dn_modify group cn add "$@"; }
l4_group_dn_edit() { entity_dn_modify group cn modify "$@" ; }
l4_group_dn_delete() { entity_dn_modify group cn delete "$@" ; }

l4_group_attr() { cmd_dispatch "$@"; }
l4_group_attr_usage() { entity_attr_usage cn; }
l4_group_attr_add() { entity_attr_modify group modify add "$@"; }
l4_group_attr_edit() { entity_attr_modify group modify replace "$@"; }
l4_group_attr_delete() { entity_attr_modify group modify delete "$@"; }

l4_group_list() { l4_extract "(objectClass=posixGroup)" "cn" |sort; }

l4_group_show() {
	min_args 1 "$@"
	local cn=$1
	shift
	l4_search "(&(cn=$cn)(objectClass=posixGroup))" "$@"
}

l4_group_info() {
	if [ -z "$*" ]; then
		with_all_groups l4_group_info
		return
	fi
	group_info Login   GID   Description
	group_info ------- ----- -------------
	{
	for g in "$@"; do
		local d
		d=$(l4_group_extract "$g" description)
		i=$(l4_group_extract "$g" gidNumber)
		group_info "$g" "$i" "$d" "${u[*]}"
	done
	} | ldap_sort
}

group_info() {
	printf "%-14s %-5s\t%-32s\t%s\n" "$@"
}

l4_group_users() {
	if [ -z "$*" ]; then
		with_all_groups l4_group_users
		return
	fi
	for g in "$@"; do
		local -a u
		u=($(l4_group_user_list "$g"))
		echo "$g: ${u[@]}"
	done
}

l4_group_add() {
	has_args 3 "$@"
	local gid name
	name=$1
	gid=$2
	desc=$3
	openldap_add <<EOF
dn: cn=$name,$ldap_groups_dn
objectClass: posixGroup
cn: $name
gidNumber: $gid
description: $desc
EOF
}


l4_group_user() { cmd_dispatch "$@"; }
l4_group_user_usage() {
	cat <<EOF
<cmd> [...]
Group User Commands:
	list <name>
	add <name> <uid>+
	delete <name> <uid>+
	modify <action> <name> <uid>+
EOF
}
l4_group_user_modify() {
	min_args 3 "$@"
	local action cn first
	action=$1
	cn=$2
	shift 2
	(
		group_dn "$cn"
		echo "changetype: modify"
		echo "$action: memberUid"
		for uid in "$@"; do
			echo "memberUid: $uid"
		done
	) | openldap_modify
}
l4_group_user_add() { l4_group_user_modify add "$@"; }
l4_group_user_delete() { l4_group_user_modify delete "$@"; }

l4_group_user_list() {
	has_args 1 "$@"
	l4_group_extract "$1" memberUid
}


######
# User CLI

l4_user() { cmd_dispatch "$@"; }
l4_user_usage() {
	cat <<EOF
<cmd> [...]
User Commands:
	info [<name>+]			Prints human readable list
	list				Prints list of user names
	ids				Prints list of user ids
	names				Prints list of user names
	email [<name>+]			Prints list of user emails

	add <name> <uid> <gid> <first> <last> [<email>] [<passwd>]
					Adds a new user
	delete <name>+			Deletes one or more users
	passwd <name>			Changes the user password

User Query Commands:
	next_id				Prints next available group ID
	exists <name>			Returns true if group exists
	show <name> [<attrs>+]		Displays group record
	id <name>			Displays group id

User Command Groups:
	dn ....				Group of DN commands
	attr ....			Group of attribute commands
EOF
}

l4_user_exists() { entity_exists user "$@"; }
l4_user_extract() { entity_extract user "$@"; }
l4_user_id() { entity_extract user "$1" uidNumber; }
l4_user_ids() { entity_ids user uidNumber "$@"; }
l4_user_next_id() { entity_next_id user; }
l4_user_delete() { entity_delete user "$@"; }

l4_user_dn() { cmd_dispatch "$@"; }
l4_user_dn_usage() { entity_dn_usage; }
l4_user_dn_add() { entity_dn_modify user uid  add "$@"; }
l4_user_dn_edit() { entity_dn_modify user uid modify "$@" ; }
l4_user_dn_delete() { entity_dn_modify user uid delete "$@" ; }

l4_user_attr() { cmd_dispatch "$@"; }
l4_user_attr_usage() { entity_attr_usage uid; }
l4_user_attr_add() { entity_attr_modify user modify add "$@"; }
l4_user_attr_edit() { entity_attr_modify user modify replace "$@"; }
l4_user_attr_delete() { entity_attr_modify user modify delete "$@"; }


l4_user_list() { l4_extract "(objectClass=posixAccount)" "uid" |sort; }

l4_user_show() {
	min_args 1 "$@"
	local uid=$1
	shift 1
	l4_search "(&(uid=$uid)(objectClass=posixAccount))" "$@"
}

l4_user_info() {
	if [ -z "$*" ]; then
		with_all_users l4_user_info
		return
	fi

	user_info Login  UID  GID  Name
	user_info ------ ---- ---- -----
	for_each_user _l4_user_info "$@" |ldap_sort
}
_l4_user_info() {
	local u=$1
	local f g
	f=$(l4_user_extract "$u" uidNumber)
	g=$(l4_user_extract "$u" gidNumber)
	local -a n
	n=($(l4_user_extract "$u" displayName))
	user_info "$u" "$f" "$g" "${n[*]}"
}
user_info() { printf "%-14s\t%5s %4s\t%s\n" "$@"; }


l4_user_names() {
	if [ -z "$*" ]; then
		with_all_users l4_user_names
		return
	fi
	user_names Login Given Surname 'Full Name' 'Display'
	user_names ----- ----- ------- --------- -----
	for_each_user _l4_user_names "$@"
}
_l4_user_names() {
	local u=$1
	local gn sn cn dn
	gn=$(l4_user_extract "$u" givenName)
	sn=$(l4_user_extract "$u" sn)
	cn=$(l4_user_extract "$u" cn)
	dn=$(l4_user_extract "$u" displayName)
	user_names "$u" "$gn" "$sn" "$cn" "$dn"
}
user_names() { printf "%-14s\t%-10s %-10s %-16s\t%-16s\n" "$@"; }



_l4_user_email() {
	local u=$1
	n=$(l4_user_extract "$u" displayName)
	e=$(l4_user_extract "$u" mail)
	echo "$n <$e>"
}
l4_user_email() {
	if [ -z "$*" ]; then
		with_all_users l4_user_email
		return
	fi
	for_each_user _l4_user_email "$@"
}


l4_user_add() {
	min_args 5 "$@"
	local login=$1
	local uid=$2
	local gid=$3
	local gn=$4
	local sn=$5
	local mail=$6
	local passwd=$7

	if [ -z "$uid" -o $uid -eq 0 ]; then
		uid=$(l4_user_next_id)
	fi
	if [ -z "$gid" -o $gid -eq 0 ]; then
		gid=$(l4_group_next_id)
	fi

	local home
	if [ "$mail" ]; then
		home="/bin/false"
	else
		mail="$login@$domain"
		home="/home/$login"
	fi
	[ "$passwd" ] || passwd=$(apg -n 1 -c "$login$uid")
	local fn="$gn $sn"
	if true; then
		user_dn $login
		cat <<EOF
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: $login
sn: $sn
givenName: $gn
cn: $fn
displayName: $fn
uidNumber: $uid
gidNumber: $gid
userPassword: $passwd
gecos: $fn
loginShell: /bin/false
homeDirectory: $home
mail: $mail
EOF
	fi | openldap_add

	l4_user_show "$login"
}


######
# Low-level commands

l4_extract() {
	local search=$1
	local field=$2
	l4_search "$search" "$field" | ldif_extract $field
}


l4_search() {
	openldap_search "$@"
}

l4_add() {
	openldap_add "$@"
}


######
# Check

run_check() { echo "=== $@ ==="; "$@"; }
l4_check() {
	if $intense; then
		run_check l4_user_info
		run_check l4_user_names
		run_check l4_user_email
		run_check l4_group_info
	else
		app_help
	fi
}


######
# Main

l4_desc() { echo "LDAP management helper"; }

l4_usage() {
	cat <<USAGE
<cmd> ...
General Commands:
	add ...				- run ldapadd
	search <query> [<field>+]	- Run a search
	extract <query> <field>		- Extract field values

Command Groups:
	user ...			User management
	group ...			Group management
	host ...			Host management
	remote ...			Remote host management
USAGE
}

l4_help() {
	cat<<HELP
The $script_name tool automates an LDAP server and directory.
HELP
}

app_run "$@"

View the Developer Guide Index

View the Reference Manual Index


Generated on Fri Jul 28 14:34:34 PDT 2017 by mcsh d14 v0.23.0.