Require current node possession proof
This commit is contained in:
parent
66eeaaf958
commit
64062fadac
10 changed files with 417 additions and 10 deletions
|
|
@ -19,6 +19,11 @@ import {
|
|||
type NodeRegistrationSnapshotReceipt,
|
||||
} from './node-registration-snapshot.js';
|
||||
import type { NodeRegistrationClaimSource } from './node-registration-client.js';
|
||||
import {
|
||||
createNodePossessionChallenge,
|
||||
verifyNodePossessionResponse,
|
||||
type NodePossessionProofSource,
|
||||
} from './node-possession-proof.js';
|
||||
|
||||
const IDENTIFIER_PATTERN = /^[A-Z0-9][A-Z0-9._:-]{1,159}$/;
|
||||
|
||||
|
|
@ -81,10 +86,13 @@ export class DomainAccessOrchestrator {
|
|||
private readonly nodeRegistrationSnapshots: NodeRegistrationSnapshotLoader,
|
||||
private readonly identities: DomainIdentitySource,
|
||||
private readonly nodeRegistrations: NodeRegistrationClaimSource,
|
||||
private readonly nodePossessions: NodePossessionProofSource,
|
||||
private readonly handoffs: DomainRuntimeHandoffSource,
|
||||
private readonly clock: () => number = Date.now,
|
||||
) {}
|
||||
|
||||
async domainAccess(domainId: string, nodeType: DomainNodeType, now = Date.now()): Promise<Readonly<DomainAccessProjection>> {
|
||||
async domainAccess(domainId: string, nodeType: DomainNodeType, at?: number): Promise<Readonly<DomainAccessProjection>> {
|
||||
let now = at ?? this.clock();
|
||||
if (!IDENTIFIER_PATTERN.test(domainId)) throw new Error('domain_access_domain_invalid');
|
||||
if (nodeType !== 'local-terminal' && nodeType !== 'cloud-resident') throw new Error('domain_access_node_type_invalid');
|
||||
const [snapshot, nodeRegistrationSnapshot, identity] = await Promise.all([
|
||||
|
|
@ -93,6 +101,7 @@ export class DomainAccessOrchestrator {
|
|||
this.identities.read(domainId, nodeType).then(safeIdentity, () => safeIdentity(null)),
|
||||
]);
|
||||
let nodeRegistrationVerified = false;
|
||||
let nodePossessionVerified = false;
|
||||
if (identity.accountVerified && nodeRegistrationSnapshot.receipt.status === 'CURRENT') {
|
||||
try {
|
||||
const loaded = assertLoadedNodeRegistrationSnapshot(nodeRegistrationSnapshot);
|
||||
|
|
@ -106,13 +115,20 @@ export class DomainAccessOrchestrator {
|
|||
nodeType,
|
||||
}));
|
||||
if (claim) {
|
||||
verifyNodeRegistrationClaim(claim, {
|
||||
const registration = verifyNodeRegistrationClaim(claim, {
|
||||
accountId: identity.accountId,
|
||||
domainId,
|
||||
nodeId: identity.nodeId,
|
||||
nodeType,
|
||||
}, endpoint, now);
|
||||
nodeRegistrationVerified = true;
|
||||
const challenge = createNodePossessionChallenge(registration, now);
|
||||
const response = await this.nodePossessions.prove(challenge);
|
||||
if (response) {
|
||||
if (at === undefined) now = this.clock();
|
||||
verifyNodePossessionResponse(response, challenge, registration, now);
|
||||
nodePossessionVerified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -123,11 +139,12 @@ export class DomainAccessOrchestrator {
|
|||
accountVerified: identity.accountVerified,
|
||||
domainId,
|
||||
nodeId: identity.nodeId,
|
||||
nodePossessionVerified,
|
||||
nodeRegistrationVerified,
|
||||
nodeType,
|
||||
};
|
||||
|
||||
if (identity.accountVerified && nodeRegistrationVerified) {
|
||||
if (identity.accountVerified && nodeRegistrationVerified && nodePossessionVerified) {
|
||||
let candidate: DomainRuntimeHandoffCandidate | null = null;
|
||||
try {
|
||||
candidate = await this.handoffs.read(domainId, identity.nodeId, nodeType);
|
||||
|
|
|
|||
Loading…
Reference in a new issue