64 lines
2.6 KiB
Python
64 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Report the Gitea account for an isolated Windows OAuth credential."""
|
|
import argparse
|
|
import io
|
|
import json
|
|
import os
|
|
import sys
|
|
from urllib.request import Request, build_opener
|
|
|
|
import credential_migrate as migration
|
|
import credential_probe as probe
|
|
import credential_win_oauth as vault
|
|
|
|
|
|
class IdentityError(Exception):
|
|
"""Contains no secret values."""
|
|
|
|
|
|
def lookup(site, namespace, opener):
|
|
try:
|
|
request = migration.checked_request(site, "account")
|
|
host = request["host"].decode("ascii")
|
|
output = io.BytesIO()
|
|
vault.run(namespace, "get", {"protocol": "https", "host": host, "username": "oauth2"}, output)
|
|
fields = vault.parse_request(io.BytesIO(output.getvalue()))
|
|
token = fields.get("password", "")
|
|
if not token or not token.isascii() or any(ord(c) < 33 or ord(c) > 126 for c in token):
|
|
raise IdentityError("The OAuth credential is unavailable.")
|
|
api_request = Request("https://" + host + "/api/v1/user", headers={"Authorization": "token " + token, "Accept": "application/json"})
|
|
with opener.open(api_request, timeout=15) as response:
|
|
body = response.read(65537)
|
|
if len(body) > 65536:
|
|
raise IdentityError("The identity response is too large.")
|
|
data = json.loads(body)
|
|
account = data.get("login") if isinstance(data, dict) else None
|
|
if not isinstance(account, str) or not account or len(account) > 256 or any(ord(c) < 32 or ord(c) == 127 for c in account):
|
|
raise IdentityError("The identity response is invalid.")
|
|
return account
|
|
except IdentityError:
|
|
raise
|
|
except Exception:
|
|
raise IdentityError("The OAuth account could not be verified safely.") from None
|
|
|
|
|
|
def main(argv=None):
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--site", required=True, help="clean HTTPS site origin")
|
|
parser.add_argument("--namespace", required=True, help="non-secret isolated vault namespace")
|
|
parser.add_argument("--repo", help="repository context for site-specific refresh configuration")
|
|
args = parser.parse_args(argv)
|
|
try:
|
|
if args.repo:
|
|
os.chdir(args.repo)
|
|
account = lookup(args.site, args.namespace, build_opener(probe.NoRedirect()))
|
|
print("Verified Gitea account: " + json.dumps(account, ensure_ascii=True))
|
|
return 0
|
|
except (IdentityError, OSError):
|
|
print("OAuth identity check could not complete; no secrets are displayed.", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|