Files
ClawSkills/git-hangman-lab/scripts/link-keycloak

141 lines
5.2 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
USERNAME=$(secret-mgr get-username --key git)
KC_PASS=$(secret-mgr get-secret --key keycloak)
GITEA_PASS=$(secret-mgr get-secret --key git)
if [[ -z "$USERNAME" || -z "$KC_PASS" || -z "$GITEA_PASS" ]]; then
echo "[ERROR] Missing required credentials in secret-mgr" >&2
exit 1
fi
sql_escape() {
printf '%s' "$1" | sed "s/'/''/g"
}
WORKDIR="/tmp"
COOKIES_FILE="$WORKDIR/${USERNAME}_oidc_cookies.txt"
KC_LOGIN_HTML="$WORKDIR/${USERNAME}_kc_login.html"
KC_POST_LOGIN_HTML="$WORKDIR/${USERNAME}_kc_post_login.html"
KC_POST_LOGIN_LOG="$WORKDIR/${USERNAME}_kc_post_login.log"
GITEA_CALLBACK_HTML="$WORKDIR/${USERNAME}_gitea_after_callback.html"
GITEA_LINK_HTML="$WORKDIR/${USERNAME}_gitea_link_account.html"
GITEA_LINK_RESP_HTML="$WORKDIR/${USERNAME}_gitea_link_signin.html"
GITEA_LINK_RESP_LOG="$WORKDIR/${USERNAME}_gitea_link_signin.log"
OIDC_URL="https://git.hangman-lab.top/user/oauth2/hangman-lab"
ESCAPED_USERNAME=$(sql_escape "$USERNAME")
cleanup() {
"$SCRIPT_DIR/external-login-ctrl" --disable >/dev/null 2>&1 || true
if [[ -n "${ORIG_LOGIN_TYPE:-}" && -n "${ORIG_LOGIN_SOURCE:-}" ]]; then
ssh root@vps.git "
set -euo pipefail
. /root/git-kc/.env
docker exec -i git-kc-mysql mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" giteadb -e \"UPDATE user SET login_type=${ORIG_LOGIN_TYPE}, login_source=${ORIG_LOGIN_SOURCE}, login_name=${ORIG_LOGIN_NAME_SQL:-NULL} WHERE name='${ESCAPED_USERNAME}';\"
" >/dev/null 2>&1 || true
fi
rm -f "$COOKIES_FILE" "$KC_LOGIN_HTML" "$KC_POST_LOGIN_HTML" "$KC_POST_LOGIN_LOG" \
"$GITEA_CALLBACK_HTML" "$GITEA_LINK_HTML" "$GITEA_LINK_RESP_HTML" "$GITEA_LINK_RESP_LOG"
}
trap cleanup EXIT
rm -f "$COOKIES_FILE" "$KC_LOGIN_HTML" "$KC_POST_LOGIN_HTML" "$KC_POST_LOGIN_LOG" \
"$GITEA_CALLBACK_HTML" "$GITEA_LINK_HTML" "$GITEA_LINK_RESP_HTML" "$GITEA_LINK_RESP_LOG"
# Capture original login fields so we can restore them exactly.
ORIG_STATE=$(ssh root@vps.git "
set -euo pipefail
. /root/git-kc/.env
docker exec -i git-kc-mysql mysql -N -B -uroot -p\"\$MYSQL_ROOT_PASSWORD\" giteadb -e \"SELECT login_type, login_source, COALESCE(login_name, '__NULL__') FROM user WHERE name='${ESCAPED_USERNAME}' LIMIT 1;\"
")
if [[ -z "$ORIG_STATE" ]]; then
echo "[ERROR] User not found in Gitea DB: $USERNAME" >&2
exit 1
fi
IFS=$'\t' read -r ORIG_LOGIN_TYPE ORIG_LOGIN_SOURCE ORIG_LOGIN_NAME <<< "$ORIG_STATE"
if [[ "$ORIG_LOGIN_NAME" == "__NULL__" ]]; then
ORIG_LOGIN_NAME_SQL="NULL"
else
ORIG_LOGIN_NAME_SQL="'$(sql_escape "$ORIG_LOGIN_NAME")'"
fi
"$SCRIPT_DIR/external-login-ctrl" --enable
echo "[INFO] 通过 OIDC 入口触发跳转,获取 Keycloak 登录页..."
curl -s -L -c "$COOKIES_FILE" "$OIDC_URL" -o "$KC_LOGIN_HTML"
KC_LOGIN_URL=$(perl -ne '
if(/<form id="kc-form-login"/ .. /<\/form>/){
if(/action="([^"]+)"/){
my $u=$1; $u=~s/&amp;/&/g; print $u; exit
}
}
' "$KC_LOGIN_HTML")
if [[ -z "${KC_LOGIN_URL:-}" ]]; then
echo "[ERROR] 未找到 kc-form-login 表单的 action URL" >&2
exit 1
fi
echo "[INFO] 登录 Keycloak..."
curl -v "$KC_LOGIN_URL" \
-b "$COOKIES_FILE" -c "$COOKIES_FILE" \
--data-urlencode "username=$USERNAME" \
--data-urlencode "password=$KC_PASS" \
-d "credentialId=" \
-o "$KC_POST_LOGIN_HTML" \
2>"$KC_POST_LOGIN_LOG" || true
GITEA_CALLBACK_URL=$(grep -i "location: https://git.hangman-lab.top" "$KC_POST_LOGIN_LOG" | sed -E 's/.*location: (https:[^\r]+).*/\1/i' | tail -n1)
if [[ -z "${GITEA_CALLBACK_URL:-}" ]]; then
echo "[ERROR] 登录后未发现返回 Gitea 的 callback URL" >&2
exit 1
fi
echo "[INFO] 调用 Gitea 回调..."
curl -s -L -b "$COOKIES_FILE" -c "$COOKIES_FILE" "$GITEA_CALLBACK_URL" -o "$GITEA_CALLBACK_HTML"
echo "[INFO] 访问 Gitea Link Account 页面..."
curl -s -b "$COOKIES_FILE" "https://git.hangman-lab.top/user/link_account" -o "$GITEA_LINK_HTML"
CSRF_TOKEN=$(perl -ne 'if(/name="_csrf" value="([^"]+)"/){print $1; exit}' "$GITEA_LINK_HTML" || true)
if [[ -z "${CSRF_TOKEN:-}" ]]; then
echo "[ERROR] 未能从 Link Account 页面解析出 _csrf" >&2
exit 1
fi
echo "[INFO] 临时将 login_type 改为本地登录..."
ssh root@vps.git "
set -euo pipefail
. /root/git-kc/.env
docker exec -i git-kc-mysql mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" giteadb -e \"UPDATE user SET login_type=0, login_source=0, login_name=NULL WHERE name='${ESCAPED_USERNAME}';\"
"
echo "[INFO] 提交 link_account_signin..."
curl -v "https://git.hangman-lab.top/user/link_account_signin" \
-b "$COOKIES_FILE" -c "$COOKIES_FILE" \
--data-urlencode "_csrf=$CSRF_TOKEN" \
--data-urlencode "user_name=$USERNAME" \
--data-urlencode "password=$GITEA_PASS" \
-o "$GITEA_LINK_RESP_HTML" \
2>"$GITEA_LINK_RESP_LOG" || true
echo "[INFO] 恢复原始登录方式..."
ssh root@vps.git "
set -euo pipefail
. /root/git-kc/.env
docker exec -i git-kc-mysql mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" giteadb -e \"UPDATE user SET login_type=${ORIG_LOGIN_TYPE}, login_source=${ORIG_LOGIN_SOURCE}, login_name=${ORIG_LOGIN_NAME_SQL} WHERE name='${ESCAPED_USERNAME}';\"
"
unset ORIG_LOGIN_TYPE ORIG_LOGIN_SOURCE ORIG_LOGIN_NAME ORIG_LOGIN_NAME_SQL
echo "[DONE] Keycloak 账号关联完成"