git@cosmocr.at/gitweb.git @ main · summary refs log
main / scripts / update-access.sh /
scripts/update-access.sh · 3.6 KiB · raw
view: Source ☐ force
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/bin/bash
# update-access.sh — edit the Access file of an upspin directory (typically a
# gitweb-hosted repo) and re-share, so the new grants actually take effect on
# already-uploaded files.
#
# Usage:
#   scripts/update-access.sh [flags] <upspin-dir>
#
#   <upspin-dir>     directory whose Access file to update,
#                    e.g. rpg137@gmail.com/myrepo.git
#   --file FILE      replace the Access file with FILE (non-interactive);
#                    default is to open the current one in $EDITOR
#   --fix-only       leave the Access file alone, only re-share (use after an
#                    Access change made by other means)
#   --force          rewrap keys regardless of current state; needed for the
#                    change to stick after REMOVING readers
#   --config FILE    upspin config to use (default $HOME/upspin/config)
#
# Why the re-share step: with ee packing, each file's decryption key is
# wrapped per reader at write time. Editing Access changes who *should* read,
# but not the wrapped keys — `upspin share -fix -r` reconciles them (adding
# the cleartext "all" entry for read:all, and per-user wraps for new grants).
#
# Revocation caveat: removing a reader (even with --force) only removes their
# wrapped key going forward; they may have already saved file keys or content.
# For real secrecy after a leak, rotate keys / re-encrypt (see `upspin rotate`
# and `upspin share -help`).
set -euo pipefail

die() { echo "update-access.sh: $*" >&2; exit 1; }

CONFIG="${HOME}/upspin/config"
FILE=""
FIXONLY=0
FORCE=0
ARGS=()
while [ $# -gt 0 ]; do
  case "$1" in
    --file) shift; FILE="${1:?--file needs a file}" ;;
    --fix-only) FIXONLY=1 ;;
    --force) FORCE=1 ;;
    --config) shift; CONFIG="${1:?--config needs a file}" ;;
    -h|--help) sed -n '2,29p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    -*) die "unknown flag $1" ;;
    *) ARGS+=("$1") ;;
  esac
  shift
done
[ "${#ARGS[@]}" -eq 1 ] || die "usage: update-access.sh [--file FILE | --fix-only] [--force] [--config FILE] <upspin-dir>"
DIR="${ARGS[0]%/}"
[ -z "$FILE" ] || [ "$FIXONLY" = 0 ] || die "--file and --fix-only are mutually exclusive"

command -v upspin >/dev/null || die "upspin not found"
[ -f "$CONFIG" ] || die "upspin config not found: $CONFIG"
case "${DIR%%/*}" in
  *@*.*) ;;
  *) die "path must start with an upspin user (user@domain/...): $DIR" ;;
esac
UP() { upspin -config="$CONFIG" "$@"; }

UP ls "$DIR" >/dev/null 2>&1 || die "$DIR does not exist or is not readable"
ACCESSPATH="$DIR/Access"

if [ "$FIXONLY" = 0 ]; then
  TMP=$(mktemp -d)
  trap 'rm -rf "$TMP"' EXIT
  NEW="$TMP/Access"
  if [ -n "$FILE" ]; then
    cp "$FILE" "$NEW"
  else
    # Start from the current Access file (empty if none exists yet — note
    # that without one, access is governed by the nearest ancestor's).
    UP get "$ACCESSPATH" > "$NEW" 2>/dev/null || : > "$NEW"
    [ -t 0 ] || die "stdin is not a terminal; use --file for non-interactive updates"
    OLDSUM=$(sha256sum "$NEW" | cut -d' ' -f1)
    ${EDITOR:-vi} "$NEW"
    if [ "$(sha256sum "$NEW" | cut -d' ' -f1)" = "$OLDSUM" ]; then
      echo "Access unchanged; re-sharing anyway"
    fi
  fi
  [ -s "$NEW" ] || die "refusing to install an empty Access file (rm $ACCESSPATH to fall back to inherited access)"
  UP cp "$NEW" "$ACCESSPATH"
  echo "==> installed $ACCESSPATH:"
  sed 's/^/    /' "$NEW"
fi

echo "==> re-sharing (rewrapping file keys to match Access)"
if [ "$FORCE" = 1 ]; then
  UP share -fix -force -r -q "$DIR"
else
  UP share -fix -r -q "$DIR"
fi

# Show the resulting state; any "keys disagree" section here means trouble.
echo "==> resulting access:"
UP share -d "$DIR" | sed 's/^/    /' | head -20