#!/usr/bin/env bash # # Create (or verify) a user's private directory under # /sf/bernina/config/personal/ - the Bernina beamline convention # for per-user storage on the shared NFS filesystem (a real NFS4 mount, # unlike ~/network_drives which is SMB and can't hold a git repo - see # gopass.md / migrate_to_network_drive.md, same directory). # # The per-user directory is locked to its owner only (mode 0700, setgid): # nobody else, not even other team members, can list or read into it. # # Usage: # create_personal_dir [username] # # Defaults to the current user. An admin can provision on someone else's # behalf by passing their username explicitly (needs write access to the # parent directory either way). # set -euo pipefail PERSONAL_ROOT="/sf/bernina/config/personal" GROUP="unx-sf_bernina_bs" TARGET_USER="${1:-$(whoami)}" TARGET_DIR="${PERSONAL_ROOT}/${TARGET_USER}" log() { echo "==> $*"; } ask() { local reply read -r -p "$1 [y/N] " reply [[ "$reply" =~ ^[Yy]$ ]] } # --- the shared parent directory must itself allow self-service ------------ # (group-write so anyone in $GROUP can create their own subdirectory, plus # the sticky bit so they can't rename/delete anyone else's - the same # principle /tmp uses). This only needs fixing once, ever, since # /sf/bernina is one NFS export shared by every Bernina host. if [[ ! -d "${PERSONAL_ROOT}" ]]; then echo "ERROR: ${PERSONAL_ROOT} does not exist - an admin needs to create it" >&2 echo " mkdir -p ${PERSONAL_ROOT} && chmod 3775 ${PERSONAL_ROOT} && chgrp ${GROUP} ${PERSONAL_ROOT}" >&2 exit 1 fi PARENT_MODE="$(stat -c '%a' "${PERSONAL_ROOT}")" PARENT_GROUP_BIT="${PARENT_MODE: -2:1}" if (( (PARENT_GROUP_BIT & 2) == 0 )); then cat >&2 <&2 exit 1 fi else echo "Ask the owner ($(stat -c '%U' "${PERSONAL_ROOT}")) to run this script once, or:" >&2 echo " chmod 3775 ${PERSONAL_ROOT} && chgrp ${GROUP} ${PERSONAL_ROOT}" >&2 exit 1 fi fi # --- create (or verify) the target user's own directory --------------------- if [[ -e "${TARGET_DIR}" ]]; then log "${TARGET_DIR} already exists" CUR_MODE="$(stat -c '%a' "${TARGET_DIR}")" CUR_OWNER="$(stat -c '%U' "${TARGET_DIR}")" CUR_GROUP="$(stat -c '%G' "${TARGET_DIR}")" if [[ "${CUR_MODE}" != "2700" || "${CUR_OWNER}" != "${TARGET_USER}" || "${CUR_GROUP}" != "${GROUP}" ]]; then log "current: mode=${CUR_MODE} owner=${CUR_OWNER} group=${CUR_GROUP}" log "expected: mode=2700 owner=${TARGET_USER} group=${GROUP}" if [[ "${CUR_OWNER}" == "$(whoami)" || "$(whoami)" == "root" ]]; then if ask "Fix permissions to match?"; then chmod 2700 "${TARGET_DIR}" [[ "$(whoami)" == "root" ]] && chown "${TARGET_USER}:${GROUP}" "${TARGET_DIR}" log "fixed" fi else echo "Not the owner - can't fix this from here." >&2 fi else log "permissions already correct (2700, ${TARGET_USER}:${GROUP})" fi else mkdir "${TARGET_DIR}" chmod 2700 "${TARGET_DIR}" chgrp "${GROUP}" "${TARGET_DIR}" 2>/dev/null || true log "created ${TARGET_DIR} ($(stat -c '%a %U %G' "${TARGET_DIR}"))" fi echo log "done. Use this for anything that needs a real (non-SMB) network filesystem" log "per user - e.g. a gopass store's --path (see gopass.md)."