rwadurian/backend/services/backup-service/src/domain/entities/backup-share.entity.ts

202 lines
5.4 KiB
TypeScript

import { DomainError } from '../errors/domain.error';
export enum BackupShareStatus {
ACTIVE = 'ACTIVE',
REVOKED = 'REVOKED',
ROTATED = 'ROTATED',
}
export interface BackupShareProps {
shareId: bigint | null;
userId: bigint;
accountSequence: bigint;
publicKey: string;
partyIndex: number;
threshold: number;
totalParties: number;
encryptedShareData: string;
encryptionKeyId: string;
status: BackupShareStatus;
accessCount: number;
lastAccessedAt: Date | null;
createdAt: Date;
updatedAt: Date;
revokedAt: Date | null;
}
export class BackupShare {
private _shareId: bigint | null;
private readonly _userId: bigint;
private readonly _accountSequence: bigint;
private readonly _publicKey: string;
private readonly _partyIndex: number;
private readonly _threshold: number;
private readonly _totalParties: number;
private _encryptedShareData: string;
private _encryptionKeyId: string;
private _status: BackupShareStatus;
private _accessCount: number;
private _lastAccessedAt: Date | null;
private readonly _createdAt: Date;
private _updatedAt: Date;
private _revokedAt: Date | null;
private constructor(props: BackupShareProps) {
this._shareId = props.shareId;
this._userId = props.userId;
this._accountSequence = props.accountSequence;
this._publicKey = props.publicKey;
this._partyIndex = props.partyIndex;
this._threshold = props.threshold;
this._totalParties = props.totalParties;
this._encryptedShareData = props.encryptedShareData;
this._encryptionKeyId = props.encryptionKeyId;
this._status = props.status;
this._accessCount = props.accessCount;
this._lastAccessedAt = props.lastAccessedAt;
this._createdAt = props.createdAt;
this._updatedAt = props.updatedAt;
this._revokedAt = props.revokedAt;
}
static create(params: {
userId: bigint;
accountSequence: bigint;
publicKey: string;
encryptedShareData: string;
encryptionKeyId: string;
threshold?: number;
totalParties?: number;
}): BackupShare {
const now = new Date();
return new BackupShare({
shareId: null,
userId: params.userId,
accountSequence: params.accountSequence,
publicKey: params.publicKey,
partyIndex: 2, // Backup = Party 2
threshold: params.threshold ?? 2,
totalParties: params.totalParties ?? 3,
encryptedShareData: params.encryptedShareData,
encryptionKeyId: params.encryptionKeyId,
status: BackupShareStatus.ACTIVE,
accessCount: 0,
lastAccessedAt: null,
createdAt: now,
updatedAt: now,
revokedAt: null,
});
}
static reconstitute(props: BackupShareProps): BackupShare {
return new BackupShare(props);
}
recordAccess(): void {
if (this._status !== BackupShareStatus.ACTIVE) {
throw new DomainError('Cannot access revoked or rotated share');
}
this._accessCount++;
this._lastAccessedAt = new Date();
this._updatedAt = new Date();
}
revoke(reason: string): void {
if (this._status === BackupShareStatus.REVOKED) {
throw new DomainError('Share already revoked');
}
this._status = BackupShareStatus.REVOKED;
this._revokedAt = new Date();
this._updatedAt = new Date();
}
rotate(newEncryptedData: string, newKeyId: string): void {
if (this._status === BackupShareStatus.REVOKED) {
throw new DomainError('Cannot rotate revoked share');
}
this._encryptedShareData = newEncryptedData;
this._encryptionKeyId = newKeyId;
this._status = BackupShareStatus.ACTIVE;
this._updatedAt = new Date();
}
isActive(): boolean {
return this._status === BackupShareStatus.ACTIVE;
}
// Getters
get shareId(): bigint | null {
return this._shareId;
}
get userId(): bigint {
return this._userId;
}
get accountSequence(): bigint {
return this._accountSequence;
}
get publicKey(): string {
return this._publicKey;
}
get partyIndex(): number {
return this._partyIndex;
}
get threshold(): number {
return this._threshold;
}
get totalParties(): number {
return this._totalParties;
}
get encryptedShareData(): string {
return this._encryptedShareData;
}
get encryptionKeyId(): string {
return this._encryptionKeyId;
}
get status(): BackupShareStatus {
return this._status;
}
get accessCount(): number {
return this._accessCount;
}
get lastAccessedAt(): Date | null {
return this._lastAccessedAt;
}
get createdAt(): Date {
return this._createdAt;
}
get updatedAt(): Date {
return this._updatedAt;
}
get revokedAt(): Date | null {
return this._revokedAt;
}
// For persistence
setShareId(id: bigint): void {
if (this._shareId !== null) {
throw new DomainError('Share ID already set');
}
this._shareId = id;
}
toProps(): BackupShareProps {
return {
shareId: this._shareId,
userId: this._userId,
accountSequence: this._accountSequence,
publicKey: this._publicKey,
partyIndex: this._partyIndex,
threshold: this._threshold,
totalParties: this._totalParties,
encryptedShareData: this._encryptedShareData,
encryptionKeyId: this._encryptionKeyId,
status: this._status,
accessCount: this._accessCount,
lastAccessedAt: this._lastAccessedAt,
createdAt: this._createdAt,
updatedAt: this._updatedAt,
revokedAt: this._revokedAt,
};
}
}