fix(gateway): tolerate legacy paired metadata in ws upgrade checks (#21447)

Fixes the pairing required regression from #21236 for legacy paired devices
created without roles/scopes metadata. Detects legacy paired metadata shape
and skips upgrade enforcement while backfilling metadata in place on reconnect.

Co-authored-by: Josh Avant <830519+joshavant@users.noreply.github.com>
Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
This commit is contained in:
Josh Avant
2026-02-19 15:45:56 -08:00
committed by GitHub
parent 7ce357ff8b
commit 29ad0736f4
3 changed files with 94 additions and 24 deletions

View File

@@ -711,43 +711,47 @@ export function attachGatewayWsMessageHandler(params: {
return;
}
} else {
const hasLegacyPairedMetadata =
paired.roles === undefined && paired.scopes === undefined;
const pairedRoles = Array.isArray(paired.roles)
? paired.roles
: paired.role
? [paired.role]
: [];
const allowedRoles = new Set(pairedRoles);
if (allowedRoles.size === 0) {
logUpgradeAudit("role-upgrade", pairedRoles, paired.scopes);
const ok = await requirePairing("role-upgrade");
if (!ok) {
return;
}
} else if (!allowedRoles.has(role)) {
logUpgradeAudit("role-upgrade", pairedRoles, paired.scopes);
const ok = await requirePairing("role-upgrade");
if (!ok) {
return;
}
}
const pairedScopes = Array.isArray(paired.scopes) ? paired.scopes : [];
if (scopes.length > 0) {
if (pairedScopes.length === 0) {
logUpgradeAudit("scope-upgrade", pairedRoles, pairedScopes);
const ok = await requirePairing("scope-upgrade");
if (!hasLegacyPairedMetadata) {
const allowedRoles = new Set(pairedRoles);
if (allowedRoles.size === 0) {
logUpgradeAudit("role-upgrade", pairedRoles, paired.scopes);
const ok = await requirePairing("role-upgrade");
if (!ok) {
return;
}
} else {
const allowedScopes = new Set(pairedScopes);
const missingScope = scopes.find((scope) => !allowedScopes.has(scope));
if (missingScope) {
} else if (!allowedRoles.has(role)) {
logUpgradeAudit("role-upgrade", pairedRoles, paired.scopes);
const ok = await requirePairing("role-upgrade");
if (!ok) {
return;
}
}
const pairedScopes = Array.isArray(paired.scopes) ? paired.scopes : [];
if (scopes.length > 0) {
if (pairedScopes.length === 0) {
logUpgradeAudit("scope-upgrade", pairedRoles, pairedScopes);
const ok = await requirePairing("scope-upgrade");
if (!ok) {
return;
}
} else {
const allowedScopes = new Set(pairedScopes);
const missingScope = scopes.find((scope) => !allowedScopes.has(scope));
if (missingScope) {
logUpgradeAudit("scope-upgrade", pairedRoles, pairedScopes);
const ok = await requirePairing("scope-upgrade");
if (!ok) {
return;
}
}
}
}
}