rin
Публичный Open-source lightweight git hosting.
git-credential-polar.sh · 110 строк · 3.4 KB
Raw
1 #!/bin/sh
2 # Git credential helper for Polar (RFC 8628 device flow).
3 #
4 # Usage:
5 # git config --global credential.helper '!sh /path/to/polar/scripts/git-credential-polar.sh'
6 #
7 # On "get" the helper reuses a cached token for the server, otherwise it starts
8 # a device authorization flow, opens the verification page in the browser and
9 # waits until the request is approved. Tokens are cached in
10 # ~/.polar/credentials.json.
11
12 op="${1:-get}"
13 base="${POLAR_URL:-}"
14 cache_dir="${HOME}/.polar"
15 cache_file="${cache_dir}/credentials.json"
16 client_id="git-credential-polar"
17
18 input="$(cat)"
19 if [ -z "$base" ]; then
20 host="$(printf '%s\n' "$input" | sed -n 's/^host=//p' | head -n1)"
21 proto="$(printf '%s\n' "$input" | sed -n 's/^protocol=//p' | head -n1)"
22 [ -z "$proto" ] && proto="https"
23 if [ -n "$host" ]; then
24 base="${proto}://${host}"
25 else
26 base="http://localhost:3100"
27 fi
28 fi
29 base="${base%/}"
30
31 json_field() {
32 # $1 = json, $2 = field
33 printf '%s' "$1" | sed -n 's/.*"'"$2"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1
34 }
35
36 case "$op" in
37 get)
38 if [ -f "$cache_file" ]; then
39 cached="$(grep -F "\"${base}\":" "$cache_file" | head -n1)"
40 if [ -n "$cached" ]; then
41 token="$(json_field "$cached" token)"
42 username="$(json_field "$cached" username)"
43 if [ -n "$token" ] && [ -n "$username" ]; then
44 printf 'username=%s\npassword=%s\n' "$username" "$token"
45 exit 0
46 fi
47 fi
48 fi
49
50 code="$(curl -fsS -X POST "${base}/api/v1/device/code" -d "client_id=${client_id}" 2>/dev/null)" || {
51 echo "polar: could not reach ${base}/api/v1/device/code" >&2
52 exit 1
53 }
54 device_code="$(json_field "$code" device_code)"
55 user_code="$(json_field "$code" user_code)"
56 verify_uri="$(json_field "$code" verification_uri)"
57 verify_full="$(json_field "$code" verification_uri_complete)"
58 interval="$(json_field "$code" interval)"
59 [ -z "$interval" ] && interval=5
60 [ "$interval" -lt 3 ] && interval=3
61
62 echo "" >&2
63 echo "polar: open ${verify_uri} and enter code ${user_code}" >&2
64 echo "polar: waiting for approval..." >&2
65 if command -v xdg-open >/dev/null 2>&1; then
66 xdg-open "$verify_full" >/dev/null 2>&1 &
67 elif command -v open >/dev/null 2>&1; then
68 open "$verify_full" >/dev/null 2>&1 &
69 fi
70
71 elapsed=0
72 while [ "$elapsed" -lt 900 ]; do
73 sleep "$interval"
74 elapsed=$((elapsed + interval))
75 resp="$(curl -sS -X POST "${base}/api/v1/device/token" -d "device_code=${device_code}&client_id=${client_id}" 2>/dev/null)"
76 token="$(json_field "$resp" access_token)"
77 if [ -n "$token" ]; then
78 username="$(json_field "$resp" username)"
79 [ -z "$username" ] && username="x-access-token"
80 mkdir -p "$cache_dir"
81 printf '{"%s":{"username":"%s","token":"%s"}}\n' "$base" "$username" "$token" >>"$cache_file"
82 printf 'username=%s\npassword=%s\n' "$username" "$token"
83 exit 0
84 fi
85 case "$resp" in
86 *authorization_pending*) continue ;;
87 *access_denied*)
88 echo "polar: access denied" >&2
89 exit 1
90 ;;
91 *expired_token*)
92 echo "polar: device code expired, run the command again" >&2
93 exit 1
94 ;;
95 *)
96 echo "polar: authorization failed: $resp" >&2
97 exit 1
98 ;;
99 esac
100 done
101 echo "polar: timed out waiting for approval" >&2
102 exit 1
103 ;;
104 store|erase)
105 exit 0
106 ;;
107 *)
108 exit 0
109 ;;
110 esac