Fix missing live Informed Decision browser registration
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
parent
651625c547
commit
b854e1ed0c
2 changed files with 119 additions and 0 deletions
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"observed_at": "2026-09-14T00:52:49.599981+00:00",
|
||||
"status": "applied_verified",
|
||||
"scope": "public browser registration only; no tokens or approval entries issued by this tool",
|
||||
"client_id": "informed-decision-approver",
|
||||
"existing_registration": false,
|
||||
"before": {
|
||||
"status": 400,
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "unknown client_id",
|
||||
"feature": "client_id"
|
||||
},
|
||||
"phase": "config_applied",
|
||||
"deployment": {
|
||||
"deployment_uid": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f",
|
||||
"generation": 52,
|
||||
"pod_uid": "0da55639-8139-4119-95f5-7415ca2a3052",
|
||||
"single_ready_replica": true
|
||||
},
|
||||
"image_unchanged": "forgejo.coulomb.social/coulomb/key-cape@sha256:db2c5a13a47839049349e881c8d19bc39f720ee69d8518f9f2eba2b1f98af9d5",
|
||||
"unrelated_config_preserved": true,
|
||||
"checks": {
|
||||
"valid_pkce": {
|
||||
"status": 302,
|
||||
"redirect_host": "auth.coulomb.social"
|
||||
},
|
||||
"wrong_redirect": {
|
||||
"status": 400,
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "redirect_uri does not match any registered URI",
|
||||
"feature": "redirect_uri"
|
||||
},
|
||||
"consume_scope": {
|
||||
"status": 400,
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "requested scope is not allowed",
|
||||
"feature": "scope"
|
||||
},
|
||||
"missing_pkce": {
|
||||
"status": 400,
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "Requests to /authorize must include a code_challenge (PKCE S256 required).",
|
||||
"feature": "missing_pkce"
|
||||
}
|
||||
},
|
||||
"review_site_login_chain": [
|
||||
{
|
||||
"status": 303,
|
||||
"redirect_host": "kc.coulomb.social"
|
||||
},
|
||||
{
|
||||
"status": 302,
|
||||
"redirect_host": "auth.coulomb.social"
|
||||
}
|
||||
]
|
||||
}
|
||||
63
tools/register-informed-decision.py
Normal file
63
tools/register-informed-decision.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""Add the admitted public browser client without exposing or replacing other config."""
|
||||
import argparse,copy,importlib.util,json,secrets,hashlib,base64
|
||||
from pathlib import Path
|
||||
from datetime import datetime,timezone
|
||||
from urllib.request import build_opener,ProxyHandler,HTTPRedirectHandler
|
||||
from urllib.parse import urlencode,urlparse
|
||||
from urllib.error import HTTPError
|
||||
import yaml
|
||||
spec=importlib.util.spec_from_file_location('rollout','/home/worsch/net-kingdom/sso-mfa/k8s/keycape/approval-clients-rollout.py');r=importlib.util.module_from_spec(spec);spec.loader.exec_module(r)
|
||||
CLIENT='informed-decision-approver';r.IDS=(CLIENT,)
|
||||
KUBE=['kubectl','--kubeconfig','/home/worsch/.kube/config-railiance01']
|
||||
EXPECTED={'clientId':CLIENT,'displayName':'informed-decision approver surface','audience':'approval-engine','redirectUris':['https://decisions.coulomb.social/auth/callback'],'allowedScopes':['openid','approval:read','approval:approve'],'grantTypes':['authorization_code'],'clientType':'public','tenant':'tenant:platform','mfaRequired':True}
|
||||
class NoRedirect(HTTPRedirectHandler):
|
||||
def redirect_request(self,*args,**kwargs):return None
|
||||
|
||||
def probe(**overrides):
|
||||
query={'response_type':'code','client_id':CLIENT,'redirect_uri':EXPECTED['redirectUris'][0],'scope':' '.join(EXPECTED['allowedScopes']),'state':secrets.token_urlsafe(32),'nonce':secrets.token_urlsafe(32),'code_challenge':base64.urlsafe_b64encode(hashlib.sha256(secrets.token_bytes(48)).digest()).rstrip(b'=').decode(),'code_challenge_method':'S256'};query.update(overrides)
|
||||
try:
|
||||
response=build_opener(ProxyHandler({}),NoRedirect()).open('https://kc.coulomb.social/authorize?'+urlencode(query),timeout=20)
|
||||
except HTTPError as e:response=e
|
||||
with response:
|
||||
result={'status':response.status}
|
||||
if response.status==302:result['redirect_host']=urlparse(response.headers['Location']).hostname
|
||||
elif response.headers.get('Content-Type','').startswith('application/json'):
|
||||
d=json.load(response);result.update({k:d.get(k) for k in ('error','description','feature')})
|
||||
return result
|
||||
|
||||
def main(args,receipt):
|
||||
r.assert_cluster(KUBE)
|
||||
candidates=[c for c in yaml.safe_load(Path('/home/worsch/key-cape/config/service-clients.example.yaml').read_text())['clients'] if c['clientId']==CLIENT]
|
||||
r.require(candidates==[EXPECTED],'public_registration_contract_drift')
|
||||
secret=r.get(KUBE,'secret','keycape-config');deployment=r.get(KUBE,'deployment','keycape')
|
||||
raw,config,_=r.pin.issuer_document(secret)
|
||||
existing=[c for c in config['clients'] if c['clientId']==CLIENT]
|
||||
receipt.update(client_id=CLIENT,existing_registration=bool(existing),before=probe())
|
||||
if existing:
|
||||
r.require(existing==[EXPECTED],'existing_registration_mismatch');receipt['status']='already_registered';return
|
||||
config_value=r.replacement(secret,[EXPECTED]);candidate=copy.deepcopy(deployment['spec'])
|
||||
image=next(c['image'] for c in candidate['template']['spec']['containers'] if c['name']=='keycape')
|
||||
r.require('@sha256:' in image and candidate['strategy']['type']=='Recreate' and candidate.get('replicas',1)==1,'deployment_pin_required')
|
||||
candidate['template'].setdefault('metadata',{}).setdefault('annotations',{})['key-cape.coulomb.social/informed-decision-registration']=datetime.now(timezone.utc).isoformat()
|
||||
r.patch_object(KUBE,'secret',secret,'/data/config.yaml',config_value,dry=True)
|
||||
r.patch_object(KUBE,'deployment',deployment,'/spec',candidate,dry=True)
|
||||
receipt['status']='preflight_passed'
|
||||
if not args.apply:return
|
||||
r.patch_object(KUBE,'secret',secret,'/data/config.yaml',config_value)
|
||||
receipt['phase']='config_applied'
|
||||
after=r.get(KUBE,'secret','keycape-config');r.require(after['data']==dict(secret['data'],**{'config.yaml':config_value}),'unrelated_config_changed')
|
||||
r.patch_object(KUBE,'deployment',deployment,'/spec',candidate)
|
||||
receipt['deployment']=r.ready(KUBE,image)
|
||||
receipt.update(image_unchanged=image,unrelated_config_preserved=True)
|
||||
checks={'valid_pkce':probe(),'wrong_redirect':probe(redirect_uri='https://example.invalid/callback'),'consume_scope':probe(scope='openid approval:consume'),'missing_pkce':probe(code_challenge='',code_challenge_method='')}
|
||||
receipt['checks']=checks
|
||||
r.require(checks['valid_pkce']['status']==302,'authorization_did_not_reach_login')
|
||||
r.require(all(checks[k]['status'] in (400,403) for k in ('wrong_redirect','consume_scope','missing_pkce')),'authorization_negative_failed')
|
||||
receipt['status']='applied_verified'
|
||||
|
||||
if __name__=='__main__':
|
||||
p=argparse.ArgumentParser();p.add_argument('--apply',action='store_true');p.add_argument('--receipt',type=Path,required=True);args=p.parse_args()
|
||||
receipt={'observed_at':datetime.now(timezone.utc).isoformat(),'status':'failed','scope':'public browser registration only; no tokens or approval entries issued by this tool'}
|
||||
try:main(args,receipt)
|
||||
except Exception:print('Registration operation failed; inspect the metadata-only receipt.');raise SystemExit(1) from None
|
||||
finally:args.receipt.write_text(json.dumps(receipt,indent=2)+'\n');print(json.dumps(receipt))
|
||||
Loading…
Add table
Add a link
Reference in a new issue