Follows the portal: the noun is "instance" everywhere the app says it — UI strings, CLI output, error details, docs and comments. The wire keys (`instance_id`, `instance_token`) and the hub route this calls move with it. An existing cloud.json is adopted rather than refused: without the key alias the dataclass fails to parse, which the caller swallows and reads as "never enrolled" instead of "reconnect". `instance_key` on a node type becomes `target_key`. It means the outside thing a node points at, which is a different sense of the word, and keeping both would put two meanings of "instance" in one codebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YrQnKV3bnQd4K342y8tKj
54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
"""A stand-in portal: its key, its JWKS, and the tokens it would mint.
|
|
|
|
Shared by the remote-access tests and the panel ones, since a screen paired
|
|
through a portal is a portal token that happens to name a panel.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import UTC, datetime, timedelta
|
|
from typing import Any
|
|
|
|
import jwt
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
|
INSTANCE_ID = "6f1c2d3e-4a5b-4c6d-8e9f-0a1b2c3d4e5f"
|
|
ISSUER = "https://hub.example.test"
|
|
|
|
|
|
def jwks(key: rsa.RSAPrivateKey) -> dict[str, Any]:
|
|
jwk = json.loads(jwt.algorithms.RSAAlgorithm.to_jwk(key.public_key()))
|
|
jwk.update({"use": "sig", "alg": "RS256", "kid": "test-portal"})
|
|
return {"keys": [jwk]}
|
|
|
|
|
|
def portal_token(
|
|
key: rsa.RSAPrivateKey,
|
|
*,
|
|
subject: str = "portal-user-1",
|
|
audience: str = INSTANCE_ID,
|
|
issuer: str = ISSUER,
|
|
scope: str = "proxy",
|
|
) -> str:
|
|
now = datetime.now(UTC)
|
|
pem = key.private_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PrivateFormat.PKCS8,
|
|
encryption_algorithm=serialization.NoEncryption(),
|
|
)
|
|
return jwt.encode(
|
|
{
|
|
"sub": subject,
|
|
"iss": issuer,
|
|
"aud": audience,
|
|
"iat": now,
|
|
"exp": now + timedelta(hours=1),
|
|
"scope": scope,
|
|
},
|
|
pem,
|
|
algorithm="RS256",
|
|
headers={"kid": "test-portal"},
|
|
)
|