Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
158 lines
8.2 KiB
Python
158 lines
8.2 KiB
Python
import base64
|
|
import contextlib
|
|
import copy
|
|
import importlib.util
|
|
import io
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
import yaml
|
|
|
|
spec = importlib.util.spec_from_file_location('playwright_registration', Path(__file__).with_name('playwright-registration.py'))
|
|
p = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(p)
|
|
IDENTITY = {'agent_id': 'test-agent', 'service_subject': 'service:test-agent:platform',
|
|
'tenant': 'tenant:test', 'identity_record': 'synthetic/agent-record.json'}
|
|
|
|
|
|
def fixture():
|
|
raw = ('# preserve the issuer and existing clients\nissuer: https://kc.coulomb.social\n'
|
|
'port: 8080\nprivateKeyPem: /synthetic/key.pem\ntokenLifetime: 15m\n'
|
|
'authelia:\n issuer: https://auth.coulomb.social\n clientSecret: synthetic-private-marker\n'
|
|
'clients:\n - clientId: existing\n clientType: public\n'
|
|
' redirectUris: [https://example.test/callback]\n'
|
|
' allowedScopes: [openid]\n grantTypes: [authorization_code]\n'
|
|
'# trailing configuration comment\nlogLevel: info\n')
|
|
return {'metadata': {'uid': 'synthetic', 'resourceVersion': '7'},
|
|
'data': {'config.yaml': base64.b64encode(raw.encode()).decode(), 'other': 'preserve'}}, raw
|
|
|
|
|
|
def decode(value):
|
|
return base64.b64decode(value).decode()
|
|
|
|
|
|
def altered(secret, change):
|
|
result = copy.deepcopy(secret)
|
|
config = yaml.safe_load(decode(result['data']['config.yaml']))
|
|
change(config)
|
|
result['data']['config.yaml'] = base64.b64encode(yaml.safe_dump(config, sort_keys=False).encode()).decode()
|
|
return result
|
|
|
|
|
|
class RegistrationTests(unittest.TestCase):
|
|
def test_public_matches_platform_and_agent_has_distinct_delivery(self):
|
|
client = p.public_client()
|
|
self.assertEqual(client['redirectUris'], ['http://127.0.0.1:18764/callback'])
|
|
self.assertNotIn('tokenLifetime', client)
|
|
self.assertNotIn('secretRef', client)
|
|
agent = p.agent_client(IDENTITY)
|
|
self.assertEqual(agent['allowedScopes'], ['playwright:connect'])
|
|
self.assertEqual(agent['roles'], ['coding-agent'])
|
|
self.assertEqual(agent['tenant'], IDENTITY['tenant'])
|
|
self.assertEqual(agent['serviceSubject'], IDENTITY['service_subject'])
|
|
self.assertEqual(agent['tokenLifetime'], '15m')
|
|
self.assertEqual(p.registry.env_binding(agent)['valueFrom']['secretKeyRef'],
|
|
{'name': 'keycape-playwright-agent-test-agent-client', 'key': 'client-secret'})
|
|
|
|
def test_unknown_or_missing_identity_fields_refused(self):
|
|
for identity in [{}, [], dict(IDENTITY, secret='not-emitted'), dict(IDENTITY, tenant=''),
|
|
dict(IDENTITY, service_subject='human:someone'),
|
|
dict(IDENTITY, identity_record='bad\nreference'),
|
|
dict(IDENTITY, agent_id='bad_$agent'), dict(IDENTITY, tenant=True)]:
|
|
with self.subTest(identity=identity), self.assertRaises(p.Refused):
|
|
p.agent_client(identity)
|
|
|
|
def test_duplicate_agent_or_subject_refused(self):
|
|
for second in [IDENTITY, dict(IDENTITY, agent_id='second'),
|
|
dict(IDENTITY, service_subject='service:second')]:
|
|
with self.subTest(second=second), self.assertRaises(p.Refused):
|
|
p.clients([IDENTITY, second])
|
|
|
|
def test_go_lifetimes_are_bounded_without_global_change(self):
|
|
for value in ['15m', '1h', '1h0m0s', '3599.5s', '1000ms', '1.5m', '.5m']:
|
|
with self.subTest(value=value): p.human_lifetime({'tokenLifetime': value})
|
|
for value in [None, 900, '', '0s', '-1m', '1h1ns', '0.5s', 'NaN', '15mgarbage', '1d']:
|
|
with self.subTest(value=value), self.assertRaises(p.Refused):
|
|
p.human_lifetime({'tokenLifetime': value})
|
|
|
|
def test_add_preserves_bytes_and_is_repeatable_then_exact_remove(self):
|
|
secret, raw = fixture()
|
|
original = copy.deepcopy(secret)
|
|
encoded, changed = p.prepare_registry(secret, [IDENTITY])
|
|
self.assertEqual(changed, ['playwright-cli', 'playwright-agent-test-agent'])
|
|
updated = decode(encoded)
|
|
prefix, suffix = raw.split('logLevel:')
|
|
self.assertTrue(updated.startswith(prefix))
|
|
self.assertTrue(updated.endswith('logLevel:' + suffix))
|
|
self.assertEqual(secret, original)
|
|
current = copy.deepcopy(secret); current['data']['config.yaml'] = encoded
|
|
self.assertEqual(p.prepare_registry(current, [IDENTITY]), (encoded, []))
|
|
restored, removed = p.prepare_registry(current, [IDENTITY], 'remove')
|
|
self.assertEqual(decode(restored), raw)
|
|
self.assertEqual(removed, changed)
|
|
self.assertEqual(p.prepare_registry(secret, [IDENTITY], 'remove'),
|
|
(secret['data']['config.yaml'], []))
|
|
|
|
def test_refuses_drift_conflicts_and_unreviewed_audience(self):
|
|
base, _ = fixture()
|
|
public = p.public_client(); agent = p.agent_client(IDENTITY)
|
|
cases = [lambda c: c.update(issuer='https://other.test'),
|
|
lambda c: c.update(tokenLifetime='2h'),
|
|
lambda c: c['clients'].append(dict(public, allowedScopes=['openid'])),
|
|
lambda c: c['clients'].append(dict(public, clientId='other-consumer')),
|
|
lambda c: c['clients'].append(dict(agent, clientId='duplicate-subject')),
|
|
lambda c: c['clients'].append({'clientId': 'other', 'secretRef': agent['secretRef']}),
|
|
lambda c: c['clients'].append(copy.deepcopy(c['clients'][0]))]
|
|
for change in cases:
|
|
for operation in ['add', 'remove']:
|
|
with self.subTest(change=change, operation=operation), self.assertRaises(p.Refused):
|
|
p.prepare_registry(altered(base, change), [IDENTITY], operation)
|
|
|
|
def test_same_agent_can_keep_separate_existing_openbao_client(self):
|
|
base, _ = fixture()
|
|
existing = dict(p.agent_client(IDENTITY), clientId='existing-openbao-agent',
|
|
audience='openbao', allowedScopes=['openbao:login'],
|
|
secretRef='env:EXISTING_OPENBAO_SECRET')
|
|
secret = altered(base, lambda c: c['clients'].append(existing))
|
|
encoded, _ = p.prepare_registry(secret, [IDENTITY])
|
|
self.assertIn(existing, yaml.safe_load(decode(encoded))['clients'])
|
|
|
|
def test_default_audience_client_id_cannot_bypass_dedicated_inventory(self):
|
|
base, _ = fixture()
|
|
default_audience = dict(p.public_client(), clientId=p.AUDIENCE)
|
|
del default_audience['audience']
|
|
secret = altered(base, lambda c: c['clients'].append(default_audience))
|
|
with self.assertRaises(p.Refused):
|
|
p.prepare_registry(secret, [IDENTITY])
|
|
|
|
def test_cli_outputs_only_proposal_and_no_false_admission(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'agents.json'; path.write_text(json.dumps([IDENTITY]))
|
|
out = io.StringIO()
|
|
with contextlib.redirect_stdout(out): code = p.main(['--agents', str(path)])
|
|
self.assertEqual(code, 0)
|
|
result = json.loads(out.getvalue())
|
|
self.assertEqual(result['status'], 'source_proposal')
|
|
for flag in ['identity_evidence_verified', 'custody_admitted', 'live_activated']:
|
|
self.assertFalse(result[flag])
|
|
self.assertEqual(len(result['clients']), 2)
|
|
self.assertEqual(result['identity_references']['playwright-agent-test-agent'],
|
|
IDENTITY['identity_record'])
|
|
|
|
def test_cli_contains_invalid_private_or_duplicate_input(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'input.json'
|
|
for raw in ['synthetic-private-marker', '[{"agent_id":"a","agent_id":"b"}]',
|
|
json.dumps([dict(IDENTITY, secret='synthetic-private-marker')]), 'x' * 65537]:
|
|
path.write_text(raw)
|
|
out = io.StringIO()
|
|
with contextlib.redirect_stdout(out): code = p.main(['--agents', str(path)])
|
|
self.assertEqual(code, 1)
|
|
self.assertNotIn('synthetic-private-marker', out.getvalue())
|
|
self.assertEqual(json.loads(out.getvalue())['status'], 'refused')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|