Files
operation-bin/create_personal_dir
lemke_h 224c68e90b bin: consistent naming, pixi_pgroup_install, git cleanup
- Commands lose their .sh extension (create_personal_dir,
  gopass_bernina_setup, gopass_bernina_service_setup,
  kerberos_ticket_renew, migrate_to_network_drive, pixi_local_sync,
  pixi_rebuild_local, sync_personal_config); .sh now only marks files
  that are sourced. kerberos_ticket_renew.sh and pixi_local_sync.sh stay
  as links, since they are started at login.
- Sourced files gain .sh (bpy-env, anaconda_env, anaconda_bernina_env,
  mm_bernina); the old names stay as links.
- All references in scripts, the .desktop file and the docs updated;
  cheatsheet.md regenerated via list_bin_commands.
- New pixi_pgroup_install: installs the shared bernina pixi env into a
  pgroup work folder (group-shared, with its own package cache) and
  makes it visible in JupyterHub as bernina-<exp>-<env>.
- Fix '#/bin/bash' first lines (missing '!') in 11 scripts.
- Stop tracking clutter (.nfs placeholders, __pycache__, vim swap file,
  .bak/_bak/~ copies, delme_*, empty 'me'); add .gitignore.
2026-09-25 15:48:11 +02:00

100 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Create (or verify) a user's private directory under
# /sf/bernina/config/personal/<username> - 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 <<EOF
NOTE: ${PERSONAL_ROOT} (mode ${PARENT_MODE}) does not allow group members
to create their own subdirectory here - only its owner can right now.
EOF
if [[ "$(stat -c '%U' "${PERSONAL_ROOT}")" == "$(whoami)" ]]; then
if ask "You own it - fix it now (chmod 3775, group ${GROUP}: setgid+sticky+group-write, so people can add their own dir but not touch anyone else's)?"; then
chmod 3775 "${PERSONAL_ROOT}"
chgrp "${GROUP}" "${PERSONAL_ROOT}"
log "fixed: $(stat -c '%a %U %G' "${PERSONAL_ROOT}")"
else
echo "ERROR: cannot self-provision without this - aborting" >&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)."