Pair a wall panel through the portal

A screen somewhere this installation is not reachable from asks the portal for
a code instead, and the portal mints its credential — because a token signed
here is one such a device could never present.

Where it was minted changes nothing about what it may do. The panel gate moved
off the branch that decodes a local panel token and onto whatever claims name
a panel, so the portal's and this installation's are bounded by the same check
against the same panel's dashboards. A token of that scope naming no panel is
refused rather than left holding the account it borrows.

The connector marks what arrives on its socket, since that is the only thing
that makes it true, and the approval screen now names what is holding a code —
approving adopts whatever answers, so it is worth a look first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017F9RnYCJgASuBTcAjxmnsp
This commit is contained in:
2026-08-20 23:42:58 +02:00
co-authored by Claude Opus 5
parent dcd6716749
commit 1d6918d8df
17 changed files with 695 additions and 175 deletions
+45 -1
View File
@@ -1,14 +1,21 @@
import uuid
from collections.abc import Generator
from datetime import UTC, datetime
from typing import Any
import pytest
from cryptography.hazmat.primitives.asymmetric import rsa
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, text
from sqlalchemy.engine import make_url
from sqlmodel import Session, SQLModel
from sqlmodel import Session, SQLModel, select
from app.cloud import config as cloud_config
from app.core.config import settings
from app.core.db import engine, init_db
from app.main import app
from app.models import User
from tests.utils.portal import INSTALLATION_ID, ISSUER, jwks
from tests.utils.user import authentication_token_from_email
from tests.utils.utils import get_superuser_token_headers
@@ -66,3 +73,40 @@ def normal_user_token_headers(client: TestClient, db: Session) -> dict[str, str]
return authentication_token_from_email(
client=client, email=settings.EMAIL_TEST_USER, db=db
)
@pytest.fixture
def portal_key() -> rsa.RSAPrivateKey:
return rsa.generate_private_key(public_exponent=65537, key_size=2048)
@pytest.fixture
def enrolled(
tmp_path_factory: pytest.TempPathFactory,
portal_key: rsa.RSAPrivateKey,
db: Session,
) -> Any:
"""Enrol this installation with a fake portal, then undo it."""
local_user = db.exec(
select(User).where(User.email == settings.FIRST_SUPERUSER)
).one()
original = settings.CLOUD_CONFIG_FILE
settings.CLOUD_CONFIG_FILE = (
tmp_path_factory.mktemp(f"cloud-{uuid.uuid4().hex[:6]}") / "cloud.json"
)
cloud_config.save(
cloud_config.CloudConfig(
portal_url=ISSUER,
ws_url=f"{ISSUER}/api/v1/tunnel/attach",
installation_id=INSTALLATION_ID,
token="installation-token",
issuer=ISSUER,
jwks=jwks(portal_key),
local_user_id=str(local_user.id),
enrolled_at=datetime.now(UTC).isoformat(),
portal_account=settings.FIRST_SUPERUSER,
)
)
yield local_user
cloud_config.delete()
settings.CLOUD_CONFIG_FILE = original