#!/usr/bin/env python3 """ probe-redmine-access.py -- What does the Redmine OIDC page actually say? Logs in via panel, clicks Redmine SSO, dumps the interaction page text verbatim. Distinguishes: login form vs consent vs "You do not have access". Usage: docker compose run --rm --entrypoint python3 \ -e AGENT=svp-knel provision probe-redmine-access.py """ import os, sys, time from pathlib import Path from playwright.sync_api import sync_playwright sys.path.insert(0, os.path.dirname(__file__)) from bw_helper import BitwardenHelper CLOUDRON_BASE = "https://my.knownelement.com" REDMINE_URL = "https://projects.knownelement.com" STATE_DIR = Path("/app/state") AGENT = os.environ.get("AGENT", "svp-knel") EMAILS = { "svp-knel": "tsgstaff-coo-svpknel@turnsys.com", "vp-secops": "tsgstaff-coo-vpsecops@turnsys.com", "coo": "tsgstaff-coo@turnsys.com", } def main(): bw = BitwardenHelper( client_id=os.environ["BW_CLIENTID"], client_secret=os.environ["BW_CLIENTSECRET"], password=os.environ["BW_PASSWORD"], server_url=os.environ.get("BW_SERVER", ""), ) bw.login() item = f"{AGENT} Cloudron" email = EMAILS[AGENT] password = bw.get_item_password(item) with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context(viewport={"width": 1280, "height": 1024}) page = context.new_page() # Panel login (networkidle like the working loop code) page.goto(f"{CLOUDRON_BASE}/login.html", wait_until="networkidle", timeout=30000) page.wait_for_timeout(2000) page.wait_for_selector("#inputPassword", timeout=15000) page.click("#inputUsername") page.keyboard.type(email) page.click("#inputPassword") page.keyboard.type(password) page.locator('[role="button"]:has-text("Log in")').first.click() page.wait_for_timeout(4000) totp = page.query_selector("#inputTotpToken") if totp and totp.is_visible(): totp.click() page.keyboard.type(bw.get_totp(item)) page.locator("#totpTokenSubmitButton").click() page.wait_for_timeout(5000) print(f"PANEL: {page.url}") # Redmine SSO page.goto(f"{REDMINE_URL}/login", wait_until="domcontentloaded", timeout=30000) page.wait_for_timeout(2000) page.locator("#login-oauth-submit-1").first.click() page.wait_for_timeout(6000) print(f"\nOIDC URL: {page.url}") body = page.evaluate("() => document.body.innerText") print("OIDC BODY (verbatim):") print(body) page.screenshot(path=str(STATE_DIR / f"redmine-access-{AGENT}-{time.strftime('%H%M%S')}.png")) browser.close() if __name__ == "__main__": main()