fix(#1356): tea login resolution fails closed on a declared git identity (#1361)
ci/woodpecker/push/publish Pipeline was canceled
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: fred <[email protected]>
This commit was merged in pull request #1361.
This commit is contained in:
@@ -180,6 +180,66 @@ raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Map a host to the instance prefix used in canonical tea login names
|
||||
# ("<instance>-<identity>"). This deliberately mirrors the _idpfx case in
|
||||
# get_gitea_token(): the two credential paths must agree on what a host is called,
|
||||
# or an agent authenticates as itself on one path and as somebody else on the other.
|
||||
gitea_instance_for_host() {
|
||||
case "${1:-}" in
|
||||
git.uscllc.com) echo usc ;;
|
||||
git.mosaicstack.dev) echo mosaicstack ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Resolve the acting git identity, same precedence as get_gitea_token() step 0.
|
||||
# Prints "<identity>\t<source>" so the caller can name the source in an error.
|
||||
resolve_git_identity() {
|
||||
local ident src
|
||||
ident="${MOSAIC_GIT_IDENTITY:-}"
|
||||
src="MOSAIC_GIT_IDENTITY"
|
||||
if [[ -z "$ident" ]]; then
|
||||
ident="$(git config --get mosaic.gitIdentity 2>/dev/null || true)"
|
||||
src="git config mosaic.gitIdentity"
|
||||
fi
|
||||
[[ -n "$ident" ]] || return 1
|
||||
printf '%s\t%s\n' "$ident" "$src"
|
||||
}
|
||||
|
||||
# Map a repo owner to an instance. Used only by the --repo override path, which
|
||||
# has an owner and no host. Previously lived inline in lane-brief.sh; one copy so
|
||||
# the two override callers cannot drift apart.
|
||||
gitea_instance_for_owner() {
|
||||
local owner="${1:-}"
|
||||
owner="${owner%%/*}"
|
||||
case "$owner" in
|
||||
usc|USC) echo usc ;;
|
||||
mosaicstack|mosaic) echo mosaicstack ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Does a login of this name exist at all? The --repo override path cannot check
|
||||
# host agreement, because it has no host.
|
||||
tea_login_exists() {
|
||||
local login_name="$1"
|
||||
local logins_json
|
||||
command -v tea >/dev/null 2>&1 || return 1
|
||||
logins_json=$(tea login list --output json 2>/dev/null) || return 1
|
||||
TEA_LOGINS_JSON="$logins_json" python3 - "$login_name" <<'PY_INNER'
|
||||
import json, os, sys
|
||||
want = sys.argv[1]
|
||||
try:
|
||||
logins = json.loads(os.environ.get("TEA_LOGINS_JSON", "[]"))
|
||||
except Exception:
|
||||
raise SystemExit(1)
|
||||
for login in logins if isinstance(logins, list) else []:
|
||||
if str(login.get("name") or login.get("Name") or "") == want:
|
||||
raise SystemExit(0)
|
||||
raise SystemExit(1)
|
||||
PY_INNER
|
||||
}
|
||||
|
||||
tea_login_matches_host() {
|
||||
local login_name="$1" host="$2"
|
||||
local logins_json
|
||||
@@ -276,6 +336,40 @@ get_gitea_login_for_host() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# IDENTITY LADDER (#1356). Below this point the old code took the FIRST login
|
||||
# matching the host, which is not an identity — with 43 logins on a fleet host,
|
||||
# ~22 match one server, so a seat with no login of its own silently acted as
|
||||
# whichever happened to be first in ~/.config/tea/config.yml. Gate 16 depends on
|
||||
# author != reviewer, and borrowing satisfies it mechanically while violating it
|
||||
# in fact. The token path already refuses to borrow; this is the same refusal.
|
||||
#
|
||||
# Enforced ONLY when an identity is resolvable, exactly like get_gitea_token():
|
||||
# no identity means a human at a terminal, and neither path enforces there.
|
||||
local ident ident_src inst canon
|
||||
if IFS=$'\t' read -r ident ident_src < <(resolve_git_identity); then
|
||||
if inst=$(gitea_instance_for_host "$host"); then
|
||||
canon="${inst}-${ident}"
|
||||
if tea_login_matches_host "$canon" "$host"; then
|
||||
echo "$canon"
|
||||
return 0
|
||||
fi
|
||||
# Say which of the two it is. "No such login" when tea is simply not
|
||||
# installed is a diagnosis of a cause that was never checked, and it
|
||||
# sends the reader off to create a login they cannot create.
|
||||
if ! command -v tea >/dev/null 2>&1; then
|
||||
echo "Error: git identity '$ident' requested (via $ident_src) for host '$host', but tea is not installed," >&2
|
||||
echo " so no login can be resolved. Refusing to guess an identity." >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Error: git identity '$ident' requested (via $ident_src) for host '$host', but no tea login named '$canon' exists." >&2
|
||||
echo " Refusing to borrow another login. Acting as a different identity would satisfy gate 16 mechanically while violating it." >&2
|
||||
echo " Create it with: ~/.config/mosaic/tools/fleet/seat-logins.sh --apply --seat $ident" >&2
|
||||
return 1
|
||||
fi
|
||||
# Identity known but the host is not a Mosaic instance. Fall through: the
|
||||
# canonical name is undefined for it, so there is nothing to enforce.
|
||||
fi
|
||||
|
||||
login=$(find_tea_login_for_host "$host" || true)
|
||||
if [[ -n "$login" ]]; then
|
||||
echo "$login"
|
||||
@@ -351,14 +445,41 @@ raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Resolve a login for an explicit --repo override, which supplies an owner and no
|
||||
# host. Takes "owner" or "owner/repo".
|
||||
#
|
||||
# The old body fell through to get_default_tea_login(), which returns the
|
||||
# default-marked login or, failing that, the first login of ANY host — arbitrary
|
||||
# identity, chosen by config file order. That is the #1356 fail-open in its worst
|
||||
# form, because unlike the host path it does not even constrain the server.
|
||||
get_gitea_login_for_repo_override() {
|
||||
local login
|
||||
local owner="${1:-}"
|
||||
local login ident ident_src inst canon
|
||||
|
||||
if [[ -n "${GITEA_LOGIN:-}" ]]; then
|
||||
echo "$GITEA_LOGIN"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if IFS=$'\t' read -r ident ident_src < <(resolve_git_identity); then
|
||||
if inst=$(gitea_instance_for_owner "$owner"); then
|
||||
canon="${inst}-${ident}"
|
||||
if tea_login_exists "$canon"; then
|
||||
echo "$canon"
|
||||
return 0
|
||||
fi
|
||||
echo "Error: git identity '$ident' (via $ident_src) has no tea login '$canon' for owner '${owner%%/*}'." >&2
|
||||
echo " Create it with: ~/.config/mosaic/tools/fleet/seat-logins.sh --apply --seat $ident" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Error: git identity '$ident' (via $ident_src) is set, but owner '${owner%%/*}' maps to no known instance," >&2
|
||||
echo " so the login name cannot be derived. Refusing to fall back to an arbitrary login." >&2
|
||||
echo " Set GITEA_LOGIN to name the login explicitly." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# No identity: a human at a terminal. Unchanged, and the same place the token
|
||||
# path stops enforcing.
|
||||
login=$(get_default_tea_login || true)
|
||||
if [[ -n "$login" ]]; then
|
||||
echo "$login"
|
||||
|
||||
Reference in New Issue
Block a user