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
|