Adds the one-session Scaleway IAM grant script, merges the publisher bucket probes into one statement to fit the 10-statement limit, keeps the Playwright cache under the account home during attended login, and records the publisher-gate runs through the num_uses rejection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 49552@bnt-lap001 Assistant-Session: 575802ee-c0c0-42a9-b2a1-9204c9f5b3f4
155 lines
4.4 KiB
Python
Executable file
155 lines
4.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Open the approved OpenBao login in a new empty Playwright Chromium profile.
|
|
|
|
The Windows default browser is not used. A previous login opened Firefox,
|
|
which was already signed in as another account. This launcher uses the
|
|
headed Chromium binary pinned by coulomb-social's Playwright install.
|
|
"""
|
|
import json
|
|
import os
|
|
import pwd
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
from urllib.parse import parse_qs, urlsplit
|
|
|
|
|
|
BROWSERS_JSON = Path(
|
|
"/home/worsch/coulomb-social/scripts/node_modules/playwright-core/browsers.json"
|
|
)
|
|
|
|
|
|
def allowed(url):
|
|
try:
|
|
parsed = urlsplit(url)
|
|
query = parse_qs(parsed.query)
|
|
return (
|
|
parsed.scheme == "https"
|
|
and parsed.hostname == "kc.coulomb.social"
|
|
and parsed.port in (None, 443)
|
|
and parsed.username is None
|
|
and parsed.password is None
|
|
and not parsed.fragment
|
|
and not any(char in url for char in "\r\n\x00")
|
|
and query.get("redirect_uri")
|
|
in (
|
|
["http://localhost:8250/oidc/callback"],
|
|
["http://127.0.0.1:8250/oidc/callback"],
|
|
)
|
|
)
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def chromium_executable(browsers_json, cache_root):
|
|
try:
|
|
spec = json.loads(Path(browsers_json).read_text(encoding="utf-8"))
|
|
except (OSError, UnicodeError, json.JSONDecodeError):
|
|
return ""
|
|
browsers = spec.get("browsers") if isinstance(spec, dict) else None
|
|
if not isinstance(browsers, list):
|
|
return ""
|
|
revision = ""
|
|
for item in browsers:
|
|
if isinstance(item, dict) and item.get("name") == "chromium":
|
|
revision = str(item.get("revision") or "")
|
|
break
|
|
if not revision.isdecimal():
|
|
return ""
|
|
candidate = Path(cache_root) / f"chromium-{revision}" / "chrome-linux64" / "chrome"
|
|
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
return str(candidate)
|
|
return ""
|
|
|
|
|
|
def playwright_cache(process_home, account_home):
|
|
"""The Chromium pin lives in the account cache.
|
|
|
|
Attended platform-admin login replaces the process home with an empty
|
|
session before OpenBao starts. Looking under that session misses the
|
|
binary, the window never opens, and the OIDC callback times out.
|
|
"""
|
|
del process_home
|
|
return Path(account_home) / ".cache" / "ms-playwright"
|
|
|
|
|
|
def browser_path():
|
|
account_home = pwd.getpwuid(os.getuid()).pw_dir
|
|
return chromium_executable(
|
|
BROWSERS_JSON, playwright_cache(Path.home(), account_home)
|
|
)
|
|
|
|
|
|
def launch_argv(browser, profile_dir, url):
|
|
return [
|
|
browser,
|
|
f"--user-data-dir={profile_dir}",
|
|
"--no-first-run",
|
|
"--no-default-browser-check",
|
|
"--disable-sync",
|
|
"--new-window",
|
|
url,
|
|
]
|
|
|
|
|
|
def classify_cmd(argv):
|
|
"""Classify OpenBao's WSL launch, `cmd.exe /c start <url>`.
|
|
|
|
hashicorp/cap replaces each & with ^& before calling the Windows
|
|
default browser. Other cmd.exe uses are left for the real binary.
|
|
"""
|
|
if list(argv[:2]) != ["/c", "start"] or len(argv) != 3:
|
|
return "delegate", ""
|
|
url = str(argv[2]).replace("^&", "&")
|
|
if allowed(url):
|
|
return "open", url
|
|
return "reject", ""
|
|
|
|
|
|
def stamp(result):
|
|
path = Path("/home/worsch/.local/state/railiance/receipts/operator-browser-last.txt")
|
|
try:
|
|
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
path.write_text(result + "\n", encoding="utf-8")
|
|
os.chmod(path, 0o600)
|
|
except OSError:
|
|
return
|
|
|
|
|
|
def open_url(url):
|
|
if not os.environ.get("DISPLAY") and not os.environ.get("WAYLAND_DISPLAY"):
|
|
stamp("no-display")
|
|
return 1
|
|
browser = browser_path()
|
|
if not browser:
|
|
stamp("no-browser")
|
|
return 1
|
|
profile = Path(tempfile.mkdtemp(prefix="operator-browser-"))
|
|
os.chmod(profile, 0o700)
|
|
command = launch_argv(browser, str(profile), url)
|
|
try:
|
|
subprocess.Popen(
|
|
command,
|
|
stdin=subprocess.DEVNULL,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
except OSError:
|
|
stamp("open-failed")
|
|
return 1
|
|
stamp("opened")
|
|
return 0
|
|
|
|
|
|
def main():
|
|
stamp("called")
|
|
if len(sys.argv) != 2 or not allowed(sys.argv[1]):
|
|
stamp("rejected")
|
|
return 1
|
|
return open_url(sys.argv[1])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|