/** * Identity implementation classes for Mail Manager services */ import type { ServiceIdentity, ServiceIdentityNone, ServiceIdentityBasic, ServiceIdentityToken, ServiceIdentityOAuth, ServiceIdentityCertificate } from '@/types/service'; import { MutationProxy } from './mutation-proxy'; import { clonePlain } from './clone-plain'; /** * Base Identity class */ export abstract class Identity { protected _original: T; protected _mutated: Partial; protected _mutationProxy: MutationProxy; protected _data: T; protected constructor(initial: T) { this._original = clonePlain(initial); this._mutated = {}; this._mutationProxy = new MutationProxy(() => this._original, () => this._mutated); this._data = this._mutationProxy.create(); } protected load(data: T): this { this._original = clonePlain(data); this._mutated = {}; this._data = this._mutationProxy.create(); return this; } toJSON(): ServiceIdentity { return this.toJson(); } toJson(): T; toJson(delta: true): Partial; toJson(delta?: boolean): T | Partial { if (delta) { return clonePlain(this._mutated); } return { ...clonePlain(this._original), ...clonePlain(this._mutated), }; } abstract clone(): Identity; mutated(): boolean { return Reflect.ownKeys(this._mutated).length > 0; } static fromJson(data: ServiceIdentity): Identity { switch (data.type) { case 'NA': return IdentityNone.fromJson(data); case 'BA': return IdentityBasic.fromJson(data); case 'TA': return IdentityToken.fromJson(data); case 'OA': return IdentityOAuth.fromJson(data); case 'CC': return IdentityCertificate.fromJson(data); default: throw new Error(`Unknown identity type: ${(data as any).type}`); } } } /** * No authentication */ export class IdentityNone extends Identity { constructor() { super({ type: 'NA' }); } static fromJson(_data: ServiceIdentityNone): IdentityNone { return new IdentityNone(); } clone(): IdentityNone { return IdentityNone.fromJson(this.toJson()); } get type(): 'NA' { return this._data.type; } } /** * Basic authentication (username/password) */ export class IdentityBasic extends Identity { constructor(identity: string = '', secret: string = '') { super({ type: 'BA', identity, secret }); } static fromJson(data: ServiceIdentityBasic): IdentityBasic { return new IdentityBasic().load(data); } clone(): IdentityBasic { return IdentityBasic.fromJson(this.toJson()); } get type(): 'BA' { return this._data.type; } get identity(): string { return this._data.identity; } set identity(value: string) { this._data.identity = value; } get secret(): string { return this._data.secret; } set secret(value: string) { this._data.secret = value; } } /** * Token authentication (API key, static token) */ export class IdentityToken extends Identity { constructor(token: string = '') { super({ type: 'TA', token }); } static fromJson(data: ServiceIdentityToken): IdentityToken { return new IdentityToken().load(data); } clone(): IdentityToken { return IdentityToken.fromJson(this.toJson()); } get type(): 'TA' { return this._data.type; } get token(): string { return this._data.token; } set token(value: string) { this._data.token = value; } } /** * OAuth authentication */ export class IdentityOAuth extends Identity { constructor( accessToken: string = '', accessScope?: string[], accessExpiry?: number, refreshToken?: string, refreshLocation?: string ) { super({ type: 'OA', accessToken, accessScope, accessExpiry, refreshToken, refreshLocation }); } static fromJson(data: ServiceIdentityOAuth): IdentityOAuth { return new IdentityOAuth().load(data); } clone(): IdentityOAuth { return IdentityOAuth.fromJson(this.toJson()); } isExpired(): boolean { if (!this.accessExpiry) return false; return Date.now() / 1000 >= this.accessExpiry; } expiresIn(): number { if (!this.accessExpiry) return Infinity; return Math.max(0, this.accessExpiry - Date.now() / 1000); } get type(): 'OA' { return this._data.type; } get accessToken(): string { return this._data.accessToken; } set accessToken(value: string) { this._data.accessToken = value; } get accessScope(): string[] | undefined { return this._data.accessScope ? [...this._data.accessScope] : undefined; } set accessScope(value: string[] | undefined) { this._data.accessScope = value ? [...value] : undefined; } get accessExpiry(): number | undefined { return this._data.accessExpiry; } set accessExpiry(value: number | undefined) { this._data.accessExpiry = value; } get refreshToken(): string | undefined { return this._data.refreshToken; } set refreshToken(value: string | undefined) { this._data.refreshToken = value; } get refreshLocation(): string | undefined { return this._data.refreshLocation; } set refreshLocation(value: string | undefined) { this._data.refreshLocation = value; } } /** * Client certificate authentication (mTLS) */ export class IdentityCertificate extends Identity { constructor(certificate: string = '', privateKey: string = '', passphrase?: string) { super({ type: 'CC', certificate, privateKey, passphrase }); } static fromJson(data: ServiceIdentityCertificate): IdentityCertificate { return new IdentityCertificate().load(data); } clone(): IdentityCertificate { return IdentityCertificate.fromJson(this.toJson()); } get type(): 'CC' { return this._data.type; } get certificate(): string { return this._data.certificate; } set certificate(value: string) { this._data.certificate = value; } get privateKey(): string { return this._data.privateKey; } set privateKey(value: string) { this._data.privateKey = value; } get passphrase(): string | undefined { return this._data.passphrase; } set passphrase(value: string | undefined) { this._data.passphrase = value; } }