import {Id} from '../shared'; // Layer token export class LayerToken { private readonly _token: string; private readonly _userId: Id; private readonly _layerId: Id; private readonly _granted: number; private readonly _mandatory: number; private readonly _exclusive: number; private readonly _created: Date; private readonly _expiry: number; private readonly _admin: boolean; private readonly _signature: string; constructor( token: string, userId: Id, layerId: Id, granted: number, mandatory: number, exclusive: number, created: Date, expiry: number, isAdmin: boolean, signature: string) { this._token = token; this._userId = userId; this._layerId = layerId; this._granted = granted; this._mandatory = mandatory; this._exclusive = exclusive; this._created = created; this._expiry = expiry; this._admin = isAdmin; this._signature = signature; } public get token(): string { return this._token; } public get userId(): string { return this._userId; } public get layerId(): string { return this._layerId; } public get granted(): number { return this._granted; } public get mandatory(): number { return this._mandatory; } public get exclusive(): number { return this._exclusive; } public get admin(): boolean { return this._admin; } public get created(): Date { return this._created; } public get expiry(): number { return this._expiry; } public get expires(): Date { return new Date(this.created.getTime() + this.expiry * 1000); } public get expired(): boolean { return this.expires < new Date(); } public get signature(): string { return this._signature; } } export function readLayerToken(tokenStr: string): LayerToken { if (tokenStr.length < 152) { throw new Error('invalid token'); } const userId = tokenStr.substring(0, 32); const spaceId = tokenStr.substring(32, 64); const granted = parseInt(tokenStr.substring(64, 72), 16); const mandatory = parseInt(tokenStr.substring(72, 80), 16); const exclusive = parseInt(tokenStr.substring(80, 88), 16); const created = new Date(parseInt(tokenStr.substring(88, 104), 16) * 1000); const expiry = parseInt(tokenStr.substring(104, 112), 16); let isAdmin = false; let signature: string; if (tokenStr.length == 154) { isAdmin = tokenStr.substring(112, 114).toLowerCase() == 'ff'; signature = tokenStr.substring(122, 154); } else { signature = tokenStr.substring(120, 152); } return new LayerToken(tokenStr, userId, spaceId, granted, mandatory, exclusive, created, expiry, isAdmin, signature); } // Access group export class AccessGroup { private _name: string; private _permissions: number; private _tokens: LayerToken[]; constructor(name: string, permissions: number) { this._name = name; this._permissions = permissions; this._tokens = []; } public registerLayerToken(layerToken: LayerToken): void { if ((layerToken.granted & this.permissions) !== this.permissions) { throw new Error('insufficient permissions'); } this._tokens.push(layerToken); } public unregisterLayerToken(token: string): void { this._tokens = this._tokens.filter((tk: LayerToken) => tk.token !== token); } public get layerId(): Id | undefined { if (this._tokens.length === 0) { return undefined; } return this._tokens[0].layerId; } public get name(): string { return this._name; } public get permissions(): number { return this._permissions; } public get tokens(): LayerToken[] { return Array.from(this._tokens); } public toString(): string { const tokens = this._tokens.map((tk) => tk.token).join(','); return `${this.name}:${this.permissions.toString(16)}:${tokens}`; } } export function readAccessGroup(groupStr: string): AccessGroup { const groupSplit = groupStr.split(':'); if (groupSplit.length !== 3) { throw new Error('invalid group string'); } const permissions = parseInt(groupSplit[1], 16); const ag = new AccessGroup(groupSplit[0], permissions); const tokenSplit = groupSplit[2].split(','); for (const token of tokenSplit) { if (!token) { continue; } ag.registerLayerToken(readLayerToken(token)); } return ag; } // Layer profile export class LayerProfile { private readonly _accessGroups: { [_: string]: AccessGroup }; constructor() { this._accessGroups = {}; } public addAccessGroup(name: string, permissions: number): AccessGroup { let ag = this._accessGroups[name]; if (!ag) { ag = new AccessGroup(name, permissions); } else if (ag.permissions !== permissions) { throw new Error('wrong permissions'); } return ag; } public getAccessGroup(name: string): AccessGroup | undefined { return this._accessGroups[name] ?? undefined; } public removeAccessGroup(name: string): void { delete this._accessGroups[name]; } public get accessGroups(): AccessGroup[] { return Array.from(Object.values(this._accessGroups)); } /** @internal */ public setAccessGroup(name: string, accessGroup: AccessGroup) { this._accessGroups[name] = accessGroup; } public toString(): string { return Object.values(this._accessGroups).map((ag) => ag.toString()).join(';'); } } export function readLayerProfile(profileStr: string): LayerProfile { const lp = new LayerProfile(); if (profileStr == '') { return lp; } const profileSplit = profileStr.split(';'); for (const accessGroupStr of profileSplit) { const ag = readAccessGroup(accessGroupStr); lp.setAccessGroup(ag.name, ag); } return lp; }