first chrome implementation
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export abstract class BrowserLocalHandler {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { BrowserSessionData } from './types';
|
||||
|
||||
export abstract class BrowserSessionHandler {
|
||||
get browserSessionData(): BrowserSessionData | undefined {
|
||||
return this.#browserSessionData;
|
||||
}
|
||||
|
||||
#browserSessionData?: BrowserSessionData;
|
||||
|
||||
/**
|
||||
* Load the data from the browser session storage. It should be an empty object,
|
||||
* if no data is available yet (e.g. because the vault (from the browser sync data)
|
||||
* was not unlocked via password).
|
||||
*
|
||||
* ATTENTION: Make sure to call "setFullData(..)" afterwards to update the in-memory data.
|
||||
*/
|
||||
abstract loadFullData(): Promise<Partial<Record<string, any>>>;
|
||||
setFullData(data: BrowserSessionData) {
|
||||
this.#browserSessionData = JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the full data to the session data storage.
|
||||
*
|
||||
* ATTENTION: Make sure to call "setFullData(..)" afterwards of before to update the in-memory data.
|
||||
*/
|
||||
abstract saveFullData(data: BrowserSessionData): Promise<void>;
|
||||
|
||||
abstract clearData(): Promise<void>;
|
||||
}
|
||||
111
projects/common/src/lib/services/storage/browser-sync-handler.ts
Normal file
111
projects/common/src/lib/services/storage/browser-sync-handler.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {
|
||||
BrowserSyncData,
|
||||
Identity_ENCRYPTED,
|
||||
Permission_ENCRYPTED,
|
||||
Relay_ENCRYPTED,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* This class handles the data that is synced between browser instances.
|
||||
* In addition to the sensitive data that is encrypted, it also contains
|
||||
* some unencrypted properties (like, version and the vault hash).
|
||||
*/
|
||||
export abstract class BrowserSyncHandler {
|
||||
get browserSyncData(): BrowserSyncData | undefined {
|
||||
return this.#browserSyncData;
|
||||
}
|
||||
|
||||
get ignoreProperties(): string[] {
|
||||
return this.#ignoreProperties;
|
||||
}
|
||||
|
||||
#browserSyncData?: BrowserSyncData;
|
||||
#ignoreProperties: string[] = [];
|
||||
|
||||
setIgnoreProperties(properties: string[]) {
|
||||
this.#ignoreProperties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load data from the sync data storage. This data might be
|
||||
* outdated (i.e. it is unmigrated), so check the unencrypted property "version" after loading.
|
||||
* Also make sure to handle the "ignore properties" (if available).
|
||||
*/
|
||||
abstract loadUnmigratedData(): Promise<Partial<Record<string, any>>>;
|
||||
|
||||
/**
|
||||
* Persist the full data to the sync data storage.
|
||||
*
|
||||
* ATTENTION: In your implementation, make sure to call "setFullData(..)" at the end to update the in-memory data.
|
||||
*/
|
||||
abstract saveAndSetFullData(data: BrowserSyncData): Promise<void>;
|
||||
|
||||
setFullData(data: BrowserSyncData) {
|
||||
this.#browserSyncData = JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the permissions to the sync data storage.
|
||||
*
|
||||
* ATTENTION: In your implementation, make sure to call "setPartialData_Permissions(..)" at the end to update the in-memory data.
|
||||
*/
|
||||
abstract saveAndSetPartialData_Permissions(data: {
|
||||
permissions: Permission_ENCRYPTED[];
|
||||
}): Promise<void>;
|
||||
setPartialData_Permissions(data: { permissions: Permission_ENCRYPTED[] }) {
|
||||
if (!this.#browserSyncData) {
|
||||
return;
|
||||
}
|
||||
this.#browserSyncData.permissions = Array.from(data.permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the identities to the sync data storage.
|
||||
*
|
||||
* ATTENTION: In your implementation, make sure to call "setPartialData_Identities(..)" at the end to update the in-memory data.
|
||||
*/
|
||||
abstract saveAndSetPartialData_Identities(data: {
|
||||
identities: Identity_ENCRYPTED[];
|
||||
}): Promise<void>;
|
||||
|
||||
setPartialData_Identities(data: { identities: Identity_ENCRYPTED[] }) {
|
||||
if (!this.#browserSyncData) {
|
||||
return;
|
||||
}
|
||||
this.#browserSyncData.identities = Array.from(data.identities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the selected identity id to the sync data storage.
|
||||
*
|
||||
* ATTENTION: In your implementation, make sure to call "setPartialData_SelectedIdentityId(..)" at the end to update the in-memory data.
|
||||
*/
|
||||
abstract saveAndSetPartialData_SelectedIdentityId(data: {
|
||||
selectedIdentityId: string | null;
|
||||
}): Promise<void>;
|
||||
|
||||
setPartialData_SelectedIdentityId(data: {
|
||||
selectedIdentityId: string | null;
|
||||
}) {
|
||||
if (!this.#browserSyncData) {
|
||||
return;
|
||||
}
|
||||
this.#browserSyncData.selectedIdentityId = data.selectedIdentityId;
|
||||
}
|
||||
|
||||
abstract saveAndSetPartialData_Relays(data: {
|
||||
relays: Relay_ENCRYPTED[];
|
||||
}): Promise<void>;
|
||||
setPartialData_Relays(data: { relays: Relay_ENCRYPTED[] }) {
|
||||
if (!this.#browserSyncData) {
|
||||
return;
|
||||
}
|
||||
this.#browserSyncData.relays = Array.from(data.relays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all data from the sync data storage.
|
||||
*/
|
||||
abstract clearData(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { BrowserSyncFlow, GootiMetaData } from './types';
|
||||
|
||||
export abstract class GootiMetaHandler {
|
||||
get gootiMetaData(): GootiMetaData | undefined {
|
||||
return this.#gootiMetaData;
|
||||
}
|
||||
|
||||
#gootiMetaData?: GootiMetaData;
|
||||
|
||||
readonly metaProperties = ['syncFlow'];
|
||||
/**
|
||||
* Load the full data from the storage. If the storage is used for storing
|
||||
* other data (e.g. browser sync data when the user decided to NOT sync),
|
||||
* make sure to handle the "meta properties" to only load these.
|
||||
*
|
||||
* ATTENTION: Make sure to call "setFullData(..)" afterwards to update the in-memory data.
|
||||
*/
|
||||
abstract loadFullData(): Promise<Partial<Record<string, any>>>;
|
||||
|
||||
setFullData(data: GootiMetaData) {
|
||||
this.#gootiMetaData = data;
|
||||
}
|
||||
|
||||
abstract saveFullData(data: GootiMetaData): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets the browser sync flow for the user and immediately saves it.
|
||||
*/
|
||||
async setBrowserSyncFlow(flow: BrowserSyncFlow): Promise<void> {
|
||||
if (!this.#gootiMetaData) {
|
||||
this.#gootiMetaData = {
|
||||
syncFlow: flow,
|
||||
};
|
||||
} else {
|
||||
this.#gootiMetaData.syncFlow = flow;
|
||||
}
|
||||
|
||||
await this.saveFullData(this.#gootiMetaData);
|
||||
}
|
||||
|
||||
abstract clearData(): Promise<void>;
|
||||
}
|
||||
232
projects/common/src/lib/services/storage/related/identity.ts
Normal file
232
projects/common/src/lib/services/storage/related/identity.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import {
|
||||
CryptoHelper,
|
||||
Identity_DECRYPTED,
|
||||
Identity_ENCRYPTED,
|
||||
NostrHelper,
|
||||
StorageService,
|
||||
} from '@common';
|
||||
|
||||
export const addIdentity = async function (
|
||||
this: StorageService,
|
||||
data: {
|
||||
nick: string;
|
||||
privkeyString: string;
|
||||
}
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const privkey = NostrHelper.getNostrPrivkeyObject(
|
||||
data.privkeyString.toLowerCase()
|
||||
).hex;
|
||||
|
||||
// Check if an identity with the same privkey already exists.
|
||||
const existingIdentity = (
|
||||
this.getBrowserSessionHandler().browserSessionData?.identities ?? []
|
||||
).find((x) => x.privkey === privkey);
|
||||
if (existingIdentity) {
|
||||
throw new Error(
|
||||
`An identity with the same private key already exists: ${existingIdentity.nick}`
|
||||
);
|
||||
}
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
if (!browserSessionData) {
|
||||
throw new Error('Browser session data is undefined.');
|
||||
}
|
||||
|
||||
const decryptedIdentity: Identity_DECRYPTED = {
|
||||
id: CryptoHelper.v4(),
|
||||
nick: data.nick,
|
||||
privkey,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Add the new identity to the session data.
|
||||
browserSessionData.identities.push(decryptedIdentity);
|
||||
let isFirstIdentity = false;
|
||||
if (browserSessionData.identities.length === 1) {
|
||||
isFirstIdentity = true;
|
||||
browserSessionData.selectedIdentityId = decryptedIdentity.id;
|
||||
}
|
||||
this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
// Encrypt the new identity and add it to the sync data.
|
||||
const encryptedIdentity = await encryptIdentity.call(this, decryptedIdentity);
|
||||
const encryptedIdentities = [
|
||||
...(this.getBrowserSyncHandler().browserSyncData?.identities ?? []),
|
||||
encryptedIdentity,
|
||||
];
|
||||
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Identities({
|
||||
identities: encryptedIdentities,
|
||||
});
|
||||
|
||||
if (isFirstIdentity) {
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_SelectedIdentityId(
|
||||
{
|
||||
selectedIdentityId: encryptedIdentity.id,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteIdentity = async function (
|
||||
this: StorageService,
|
||||
identityId: string | undefined
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
if (!identityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
const browserSyncData = this.getBrowserSyncHandler().browserSyncData;
|
||||
if (!browserSessionData || !browserSyncData) {
|
||||
throw new Error('Browser session or sync data is undefined.');
|
||||
}
|
||||
|
||||
browserSessionData.identities = browserSessionData.identities.filter(
|
||||
(x) => x.id !== identityId
|
||||
);
|
||||
browserSessionData.permissions = browserSessionData.permissions.filter(
|
||||
(x) => x.identityId !== identityId
|
||||
);
|
||||
browserSessionData.relays = browserSessionData.relays.filter(
|
||||
(x) => x.identityId !== identityId
|
||||
);
|
||||
if (browserSessionData.selectedIdentityId === identityId) {
|
||||
// Choose another identity to be selected or null if there is none.
|
||||
browserSessionData.selectedIdentityId =
|
||||
browserSessionData.identities.length > 0
|
||||
? browserSessionData.identities[0].id
|
||||
: null;
|
||||
}
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
// Handle Sync data.
|
||||
const encryptedIdentityId = await this.encrypt(identityId);
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Identities({
|
||||
identities: browserSyncData.identities.filter(
|
||||
(x) => x.id !== encryptedIdentityId
|
||||
),
|
||||
});
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Permissions({
|
||||
permissions: browserSyncData.permissions.filter(
|
||||
(x) => x.identityId !== encryptedIdentityId
|
||||
),
|
||||
});
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Relays({
|
||||
relays: browserSyncData.relays.filter(
|
||||
(x) => x.identityId !== encryptedIdentityId
|
||||
),
|
||||
});
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_SelectedIdentityId({
|
||||
selectedIdentityId:
|
||||
browserSessionData.selectedIdentityId === null
|
||||
? null
|
||||
: await this.encrypt(browserSessionData.selectedIdentityId),
|
||||
});
|
||||
};
|
||||
|
||||
export const switchIdentity = async function (
|
||||
this: StorageService,
|
||||
identityId: string | null
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
// Check, if the identity really exists.
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
|
||||
if (!browserSessionData?.identities.find((x) => x.id === identityId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
browserSessionData.selectedIdentityId = identityId;
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
const encryptedIdentityId =
|
||||
identityId === null ? null : await this.encrypt(identityId);
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_SelectedIdentityId({
|
||||
selectedIdentityId: encryptedIdentityId,
|
||||
});
|
||||
};
|
||||
|
||||
export const encryptIdentity = async function (
|
||||
this: StorageService,
|
||||
identity: Identity_DECRYPTED
|
||||
): Promise<Identity_ENCRYPTED> {
|
||||
const encryptedIdentity: Identity_ENCRYPTED = {
|
||||
id: await this.encrypt(identity.id),
|
||||
nick: await this.encrypt(identity.nick),
|
||||
createdAt: await this.encrypt(identity.createdAt),
|
||||
privkey: await this.encrypt(identity.privkey),
|
||||
};
|
||||
|
||||
return encryptedIdentity;
|
||||
};
|
||||
|
||||
export const decryptIdentities = async function (
|
||||
this: StorageService,
|
||||
identities: Identity_ENCRYPTED[],
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Identity_DECRYPTED[]> {
|
||||
const decryptedIdentities: Identity_DECRYPTED[] = [];
|
||||
|
||||
for (const identity of identities) {
|
||||
const decryptedIdentity = await decryptIdentity.call(
|
||||
this,
|
||||
identity,
|
||||
withLockedVault
|
||||
);
|
||||
decryptedIdentities.push(decryptedIdentity);
|
||||
}
|
||||
|
||||
return decryptedIdentities;
|
||||
};
|
||||
|
||||
export const decryptIdentity = async function (
|
||||
this: StorageService,
|
||||
identity: Identity_ENCRYPTED,
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Identity_DECRYPTED> {
|
||||
if (typeof withLockedVault === 'undefined') {
|
||||
const decryptedIdentity: Identity_DECRYPTED = {
|
||||
id: await this.decrypt(identity.id, 'string'),
|
||||
nick: await this.decrypt(identity.nick, 'string'),
|
||||
createdAt: await this.decrypt(identity.createdAt, 'string'),
|
||||
privkey: await this.decrypt(identity.privkey, 'string'),
|
||||
};
|
||||
|
||||
return decryptedIdentity;
|
||||
}
|
||||
|
||||
const decryptedIdentity: Identity_DECRYPTED = {
|
||||
id: await this.decryptWithLockedVault(
|
||||
identity.id,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
nick: await this.decryptWithLockedVault(
|
||||
identity.nick,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
createdAt: await this.decryptWithLockedVault(
|
||||
identity.createdAt,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
privkey: await this.decryptWithLockedVault(
|
||||
identity.privkey,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
};
|
||||
|
||||
return decryptedIdentity;
|
||||
};
|
||||
111
projects/common/src/lib/services/storage/related/permission.ts
Normal file
111
projects/common/src/lib/services/storage/related/permission.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
Permission_DECRYPTED,
|
||||
Permission_ENCRYPTED,
|
||||
StorageService,
|
||||
} from '@common';
|
||||
|
||||
export const deletePermission = async function (
|
||||
this: StorageService,
|
||||
permissionId: string
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
const browserSyncData = this.getBrowserSyncHandler().browserSyncData;
|
||||
if (!browserSessionData || !browserSyncData) {
|
||||
throw new Error('Browser session or sync data is undefined.');
|
||||
}
|
||||
|
||||
browserSessionData.permissions = browserSessionData.permissions.filter(
|
||||
(x) => x.id !== permissionId
|
||||
);
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
const encryptedPermissionId = await this.encrypt(permissionId);
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Permissions({
|
||||
permissions: browserSyncData.permissions.filter(
|
||||
(x) => x.id !== encryptedPermissionId
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
export const decryptPermission = async function (
|
||||
this: StorageService,
|
||||
permission: Permission_ENCRYPTED,
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Permission_DECRYPTED> {
|
||||
if (typeof withLockedVault === 'undefined') {
|
||||
const decryptedPermission: Permission_DECRYPTED = {
|
||||
id: await this.decrypt(permission.id, 'string'),
|
||||
identityId: await this.decrypt(permission.identityId, 'string'),
|
||||
method: await this.decrypt(permission.method, 'string'),
|
||||
methodPolicy: await this.decrypt(permission.methodPolicy, 'string'),
|
||||
host: await this.decrypt(permission.host, 'string'),
|
||||
};
|
||||
if (permission.kind) {
|
||||
decryptedPermission.kind = await this.decrypt(permission.kind, 'number');
|
||||
}
|
||||
return decryptedPermission;
|
||||
}
|
||||
|
||||
const decryptedPermission: Permission_DECRYPTED = {
|
||||
id: await this.decryptWithLockedVault(
|
||||
permission.id,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
identityId: await this.decryptWithLockedVault(
|
||||
permission.identityId,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
method: await this.decryptWithLockedVault(
|
||||
permission.method,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
methodPolicy: await this.decryptWithLockedVault(
|
||||
permission.methodPolicy,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
host: await this.decryptWithLockedVault(
|
||||
permission.host,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
};
|
||||
if (permission.kind) {
|
||||
decryptedPermission.kind = await this.decryptWithLockedVault(
|
||||
permission.kind,
|
||||
'number',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
);
|
||||
}
|
||||
return decryptedPermission;
|
||||
};
|
||||
|
||||
export const decryptPermissions = async function (
|
||||
this: StorageService,
|
||||
permissions: Permission_ENCRYPTED[],
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Permission_DECRYPTED[]> {
|
||||
const decryptedPermissions: Permission_DECRYPTED[] = [];
|
||||
|
||||
for (const permission of permissions) {
|
||||
const decryptedPermission = await decryptPermission.call(
|
||||
this,
|
||||
permission,
|
||||
withLockedVault
|
||||
);
|
||||
decryptedPermissions.push(decryptedPermission);
|
||||
}
|
||||
|
||||
return decryptedPermissions;
|
||||
};
|
||||
209
projects/common/src/lib/services/storage/related/relay.ts
Normal file
209
projects/common/src/lib/services/storage/related/relay.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import {
|
||||
CryptoHelper,
|
||||
Relay_DECRYPTED,
|
||||
Relay_ENCRYPTED,
|
||||
StorageService,
|
||||
} from '@common';
|
||||
|
||||
export const addRelay = async function (
|
||||
this: StorageService,
|
||||
data: {
|
||||
identityId: string;
|
||||
url: string;
|
||||
write: boolean;
|
||||
read: boolean;
|
||||
}
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
// Check, if a relay with the same URL already exists for the identity.
|
||||
const existingRelay =
|
||||
this.getBrowserSessionHandler().browserSessionData?.relays.find(
|
||||
(x) =>
|
||||
x.url.toLowerCase() === data.url.toLowerCase() &&
|
||||
x.identityId === data.identityId
|
||||
);
|
||||
if (existingRelay) {
|
||||
throw new Error('A relay with the same URL already exists.');
|
||||
}
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
if (!browserSessionData) {
|
||||
throw new Error('Browser session data is undefined.');
|
||||
}
|
||||
|
||||
const decryptedRelay: Relay_DECRYPTED = {
|
||||
id: CryptoHelper.v4(),
|
||||
identityId: data.identityId,
|
||||
url: data.url,
|
||||
write: data.write,
|
||||
read: data.read,
|
||||
};
|
||||
|
||||
// Add the new relay to the session data.
|
||||
browserSessionData.relays.push(decryptedRelay);
|
||||
this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
// Encrypt the new relay and add it to the sync data.
|
||||
const encryptedRelay = await encryptRelay.call(this, decryptedRelay);
|
||||
const encryptedRelays = [
|
||||
...(this.getBrowserSyncHandler().browserSyncData?.relays ?? []),
|
||||
encryptedRelay,
|
||||
];
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Relays({
|
||||
relays: encryptedRelays,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteRelay = async function (
|
||||
this: StorageService,
|
||||
relayId: string
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
if (!relayId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
const browserSyncData = this.getBrowserSyncHandler().browserSyncData;
|
||||
if (!browserSessionData || !browserSyncData) {
|
||||
throw new Error('Browser session or sync data is undefined.');
|
||||
}
|
||||
|
||||
browserSessionData.relays = browserSessionData.relays.filter(
|
||||
(x) => x.id !== relayId
|
||||
);
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
// Handle Sync data.
|
||||
const encryptedRelayId = await this.encrypt(relayId);
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Relays({
|
||||
relays: browserSyncData.relays.filter((x) => x.id !== encryptedRelayId),
|
||||
});
|
||||
};
|
||||
|
||||
export const updateRelay = async function (
|
||||
this: StorageService,
|
||||
relayClone: Relay_DECRYPTED
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
const browserSyncData = this.getBrowserSyncHandler().browserSyncData;
|
||||
if (!browserSessionData || !browserSyncData) {
|
||||
throw new Error('Browser session or sync data is undefined.');
|
||||
}
|
||||
|
||||
const sessionRelay = browserSessionData.relays.find(
|
||||
(x) => x.id === relayClone.id
|
||||
);
|
||||
const encryptedRelayId = await this.encrypt(relayClone.id);
|
||||
const syncRelay = browserSyncData.relays.find(
|
||||
(x) => x.id === encryptedRelayId
|
||||
);
|
||||
if (!sessionRelay || !syncRelay) {
|
||||
throw new Error(
|
||||
'Relay not found in browser session or sync data for update.'
|
||||
);
|
||||
}
|
||||
|
||||
// Handle Session update.
|
||||
sessionRelay.read = relayClone.read;
|
||||
sessionRelay.write = relayClone.write;
|
||||
sessionRelay.url = relayClone.url;
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
|
||||
// Handle Sync update.
|
||||
syncRelay.read = await this.encrypt(relayClone.read.toString());
|
||||
syncRelay.write = await this.encrypt(relayClone.write.toString());
|
||||
syncRelay.url = await this.encrypt(relayClone.url);
|
||||
await this.getBrowserSyncHandler().saveAndSetPartialData_Relays({
|
||||
relays: browserSyncData.relays,
|
||||
});
|
||||
};
|
||||
|
||||
export const decryptRelay = async function (
|
||||
this: StorageService,
|
||||
relay: Relay_ENCRYPTED,
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Relay_DECRYPTED> {
|
||||
if (typeof withLockedVault === 'undefined') {
|
||||
const decryptedRelay: Relay_DECRYPTED = {
|
||||
id: await this.decrypt(relay.id, 'string'),
|
||||
identityId: await this.decrypt(relay.identityId, 'string'),
|
||||
url: await this.decrypt(relay.url, 'string'),
|
||||
read: await this.decrypt(relay.read, 'boolean'),
|
||||
write: await this.decrypt(relay.write, 'boolean'),
|
||||
};
|
||||
return decryptedRelay;
|
||||
}
|
||||
|
||||
const decryptedRelay: Relay_DECRYPTED = {
|
||||
id: await this.decryptWithLockedVault(
|
||||
relay.id,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
identityId: await this.decryptWithLockedVault(
|
||||
relay.identityId,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
url: await this.decryptWithLockedVault(
|
||||
relay.url,
|
||||
'string',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
read: await this.decryptWithLockedVault(
|
||||
relay.read,
|
||||
'boolean',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
write: await this.decryptWithLockedVault(
|
||||
relay.write,
|
||||
'boolean',
|
||||
withLockedVault.iv,
|
||||
withLockedVault.password
|
||||
),
|
||||
};
|
||||
return decryptedRelay;
|
||||
};
|
||||
|
||||
export const decryptRelays = async function (
|
||||
this: StorageService,
|
||||
relays: Relay_ENCRYPTED[],
|
||||
withLockedVault: { iv: string; password: string } | undefined = undefined
|
||||
): Promise<Relay_DECRYPTED[]> {
|
||||
const decryptedRelays: Relay_DECRYPTED[] = [];
|
||||
|
||||
for (const relay of relays) {
|
||||
const decryptedRelay = await decryptRelay.call(
|
||||
this,
|
||||
relay,
|
||||
withLockedVault
|
||||
);
|
||||
decryptedRelays.push(decryptedRelay);
|
||||
}
|
||||
|
||||
return decryptedRelays;
|
||||
};
|
||||
|
||||
export const encryptRelay = async function (
|
||||
this: StorageService,
|
||||
relay: Relay_DECRYPTED
|
||||
): Promise<Relay_ENCRYPTED> {
|
||||
const encryptedRelay: Relay_ENCRYPTED = {
|
||||
id: await this.encrypt(relay.id),
|
||||
identityId: await this.encrypt(relay.identityId),
|
||||
url: await this.encrypt(relay.url),
|
||||
read: await this.encrypt(relay.read.toString()),
|
||||
write: await this.encrypt(relay.write.toString()),
|
||||
};
|
||||
|
||||
return encryptedRelay;
|
||||
};
|
||||
128
projects/common/src/lib/services/storage/related/vault.ts
Normal file
128
projects/common/src/lib/services/storage/related/vault.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
BrowserSessionData,
|
||||
BrowserSyncData,
|
||||
CryptoHelper,
|
||||
StorageService,
|
||||
} from '@common';
|
||||
import { decryptIdentities } from './identity';
|
||||
import { decryptPermissions } from './permission';
|
||||
import { decryptRelays } from './relay';
|
||||
|
||||
export const createNewVault = async function (
|
||||
this: StorageService,
|
||||
password: string
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const vaultHash = await CryptoHelper.hash(password);
|
||||
|
||||
const sessionData: BrowserSessionData = {
|
||||
iv: CryptoHelper.generateIV(),
|
||||
vaultPassword: password,
|
||||
identities: [],
|
||||
permissions: [],
|
||||
relays: [],
|
||||
selectedIdentityId: null,
|
||||
};
|
||||
await this.getBrowserSessionHandler().saveFullData(sessionData);
|
||||
this.getBrowserSessionHandler().setFullData(sessionData);
|
||||
|
||||
const syncData: BrowserSyncData = {
|
||||
version: this.latestVersion,
|
||||
iv: sessionData.iv,
|
||||
vaultHash,
|
||||
identities: [],
|
||||
permissions: [],
|
||||
relays: [],
|
||||
selectedIdentityId: null,
|
||||
};
|
||||
await this.getBrowserSyncHandler().saveAndSetFullData(syncData);
|
||||
};
|
||||
|
||||
export const unlockVault = async function (
|
||||
this: StorageService,
|
||||
password: string
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
let browserSessionData = this.getBrowserSessionHandler().browserSessionData;
|
||||
if (browserSessionData) {
|
||||
throw new Error(
|
||||
'Browser session data is available. Should only happen when the vault is unlocked'
|
||||
);
|
||||
}
|
||||
|
||||
const browserSyncData = this.getBrowserSyncHandler().browserSyncData;
|
||||
if (!browserSyncData) {
|
||||
throw new Error(
|
||||
'Browser sync data is not available. Should have been loaded before.'
|
||||
);
|
||||
}
|
||||
|
||||
const passwordHash = await CryptoHelper.hash(password);
|
||||
if (passwordHash !== browserSyncData.vaultHash) {
|
||||
throw new Error('Invalid password.');
|
||||
}
|
||||
|
||||
// Ok. Everything is fine. We can unlock the vault now.
|
||||
|
||||
// Decrypt the identities.
|
||||
const withLockedVault = {
|
||||
iv: browserSyncData.iv,
|
||||
password,
|
||||
};
|
||||
const decryptedIdentities = await decryptIdentities.call(
|
||||
this,
|
||||
browserSyncData.identities,
|
||||
withLockedVault
|
||||
);
|
||||
const decryptedPermissions = await decryptPermissions.call(
|
||||
this,
|
||||
browserSyncData.permissions,
|
||||
withLockedVault
|
||||
);
|
||||
const decryptedRelays = await decryptRelays.call(
|
||||
this,
|
||||
browserSyncData.relays,
|
||||
withLockedVault
|
||||
);
|
||||
const decryptedSelectedIdentityId =
|
||||
browserSyncData.selectedIdentityId === null
|
||||
? null
|
||||
: await this.decryptWithLockedVault(
|
||||
browserSyncData.selectedIdentityId,
|
||||
'string',
|
||||
browserSyncData.iv,
|
||||
password
|
||||
);
|
||||
|
||||
browserSessionData = {
|
||||
vaultPassword: password,
|
||||
iv: browserSyncData.iv,
|
||||
permissions: decryptedPermissions,
|
||||
identities: decryptedIdentities,
|
||||
selectedIdentityId: decryptedSelectedIdentityId,
|
||||
relays: decryptedRelays,
|
||||
};
|
||||
await this.getBrowserSessionHandler().saveFullData(browserSessionData);
|
||||
this.getBrowserSessionHandler().setFullData(browserSessionData);
|
||||
};
|
||||
|
||||
export const deleteVault = async function (
|
||||
this: StorageService,
|
||||
doNotSetIsInitializedToFalse: boolean
|
||||
): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
const syncFlow = this.getGootiMetaHandler().gootiMetaData?.syncFlow;
|
||||
if (typeof syncFlow === 'undefined') {
|
||||
throw new Error('Sync flow is not set.');
|
||||
}
|
||||
|
||||
await this.getBrowserSyncHandler().clearData();
|
||||
await this.getBrowserSessionHandler().clearData();
|
||||
await this.getGootiMetaHandler().clearData();
|
||||
|
||||
if (!doNotSetIsInitializedToFalse) {
|
||||
this.isInitialized = false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
describe('StorageService', () => {
|
||||
let service: StorageService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(StorageService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
335
projects/common/src/lib/services/storage/storage.service.ts
Normal file
335
projects/common/src/lib/services/storage/storage.service.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BrowserSyncHandler } from './browser-sync-handler';
|
||||
import { BrowserSessionHandler } from './browser-session-handler';
|
||||
import {
|
||||
BrowserSessionData,
|
||||
BrowserSyncData,
|
||||
BrowserSyncFlow,
|
||||
GootiMetaData,
|
||||
Relay_DECRYPTED,
|
||||
} from './types';
|
||||
import { GootiMetaHandler } from './gooti-meta-handler';
|
||||
import { CryptoHelper } from '@common';
|
||||
import {
|
||||
addIdentity,
|
||||
deleteIdentity,
|
||||
switchIdentity,
|
||||
} from './related/identity';
|
||||
import { deletePermission } from './related/permission';
|
||||
import { createNewVault, deleteVault, unlockVault } from './related/vault';
|
||||
import { addRelay, deleteRelay, updateRelay } from './related/relay';
|
||||
|
||||
export interface StorageServiceConfig {
|
||||
browserSessionHandler: BrowserSessionHandler;
|
||||
browserSyncYesHandler: BrowserSyncHandler;
|
||||
browserSyncNoHandler: BrowserSyncHandler;
|
||||
gootiMetaHandler: GootiMetaHandler;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class StorageService {
|
||||
readonly latestVersion = 1;
|
||||
isInitialized = false;
|
||||
|
||||
#browserSessionHandler!: BrowserSessionHandler;
|
||||
#browserSyncYesHandler!: BrowserSyncHandler;
|
||||
#browserSyncNoHandler!: BrowserSyncHandler;
|
||||
#gootiMetaHandler!: GootiMetaHandler;
|
||||
|
||||
initialize(config: StorageServiceConfig): void {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
this.#browserSessionHandler = config.browserSessionHandler;
|
||||
this.#browserSyncYesHandler = config.browserSyncYesHandler;
|
||||
this.#browserSyncNoHandler = config.browserSyncNoHandler;
|
||||
this.#gootiMetaHandler = config.gootiMetaHandler;
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
async enableBrowserSyncFlow(flow: BrowserSyncFlow): Promise<void> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
this.#gootiMetaHandler.setBrowserSyncFlow(flow);
|
||||
}
|
||||
|
||||
async loadGootiMetaData(): Promise<GootiMetaData | undefined> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const data = await this.#gootiMetaHandler.loadFullData();
|
||||
if (Object.keys(data).length === 0) {
|
||||
// No data available yet.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.#gootiMetaHandler.setFullData(data as GootiMetaData);
|
||||
return data as GootiMetaData;
|
||||
}
|
||||
|
||||
async loadBrowserSessionData(): Promise<BrowserSessionData | undefined> {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const data = await this.#browserSessionHandler.loadFullData();
|
||||
if (Object.keys(data).length === 0) {
|
||||
// No data available yet (e.g. because the vault was not unlocked).
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Set the existing data for in-memory usage.
|
||||
this.#browserSessionHandler.setFullData(data as BrowserSessionData);
|
||||
return data as BrowserSessionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and migrate the browser sync data. If no data is available yet,
|
||||
* the returned object is undefined.
|
||||
*/
|
||||
async loadAndMigrateBrowserSyncData(): Promise<BrowserSyncData | undefined> {
|
||||
this.assureIsInitialized();
|
||||
const unmigratedBrowserSyncData =
|
||||
await this.getBrowserSyncHandler().loadUnmigratedData();
|
||||
const { browserSyncData, migrationWasPerformed } =
|
||||
this.#migrateBrowserSyncData(unmigratedBrowserSyncData);
|
||||
|
||||
if (!browserSyncData) {
|
||||
// Nothing to do at this point.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// There is data. Check, if it was migrated.
|
||||
if (migrationWasPerformed) {
|
||||
// Persist the migrated data back to the browser sync storage.
|
||||
this.getBrowserSyncHandler().saveAndSetFullData(browserSyncData);
|
||||
} else {
|
||||
// Set the data for in-memory usage.
|
||||
this.getBrowserSyncHandler().setFullData(browserSyncData);
|
||||
}
|
||||
|
||||
return browserSyncData;
|
||||
}
|
||||
|
||||
async deleteVault(doNotSetIsInitializedToFalse = false) {
|
||||
await deleteVault.call(this, doNotSetIsInitializedToFalse);
|
||||
}
|
||||
|
||||
async unlockVault(password: string): Promise<void> {
|
||||
await unlockVault.call(this, password);
|
||||
}
|
||||
|
||||
async createNewVault(password: string): Promise<void> {
|
||||
await createNewVault.call(this, password);
|
||||
}
|
||||
|
||||
async addIdentity(data: {
|
||||
nick: string;
|
||||
privkeyString: string;
|
||||
}): Promise<void> {
|
||||
await addIdentity.call(this, data);
|
||||
}
|
||||
|
||||
async deleteIdentity(identityId: string | undefined): Promise<void> {
|
||||
await deleteIdentity.call(this, identityId);
|
||||
}
|
||||
|
||||
async switchIdentity(identityId: string | null): Promise<void> {
|
||||
await switchIdentity.call(this, identityId);
|
||||
}
|
||||
|
||||
async deletePermission(permissionId: string) {
|
||||
await deletePermission.call(this, permissionId);
|
||||
}
|
||||
|
||||
async addRelay(data: {
|
||||
identityId: string;
|
||||
url: string;
|
||||
write: boolean;
|
||||
read: boolean;
|
||||
}): Promise<void> {
|
||||
await addRelay.call(this, data);
|
||||
}
|
||||
|
||||
async deleteRelay(relayId: string): Promise<void> {
|
||||
await deleteRelay.call(this, relayId);
|
||||
}
|
||||
|
||||
async updateRelay(relayClone: Relay_DECRYPTED): Promise<void> {
|
||||
await updateRelay.call(this, relayClone);
|
||||
}
|
||||
|
||||
exportVault(): string {
|
||||
this.assureIsInitialized();
|
||||
const vaultJson = JSON.stringify(
|
||||
this.getBrowserSyncHandler().browserSyncData,
|
||||
undefined,
|
||||
4
|
||||
);
|
||||
return vaultJson;
|
||||
}
|
||||
|
||||
async importVault(allegedBrowserSyncData: BrowserSyncData) {
|
||||
this.assureIsInitialized();
|
||||
|
||||
const isValidData = this.#allegedBrowserSyncDataIsValid(
|
||||
allegedBrowserSyncData
|
||||
);
|
||||
if (!isValidData) {
|
||||
throw new Error('The imported data is not valid.');
|
||||
}
|
||||
|
||||
await this.getBrowserSyncHandler().saveAndSetFullData(
|
||||
allegedBrowserSyncData
|
||||
);
|
||||
}
|
||||
|
||||
getBrowserSyncHandler(): BrowserSyncHandler {
|
||||
this.assureIsInitialized();
|
||||
|
||||
switch (this.#gootiMetaHandler.gootiMetaData?.syncFlow) {
|
||||
case BrowserSyncFlow.NO_SYNC:
|
||||
return this.#browserSyncNoHandler;
|
||||
|
||||
case BrowserSyncFlow.BROWSER_SYNC:
|
||||
default:
|
||||
return this.#browserSyncYesHandler;
|
||||
}
|
||||
}
|
||||
|
||||
getBrowserSessionHandler(): BrowserSessionHandler {
|
||||
this.assureIsInitialized();
|
||||
|
||||
return this.#browserSessionHandler;
|
||||
}
|
||||
|
||||
getGootiMetaHandler(): GootiMetaHandler {
|
||||
this.assureIsInitialized();
|
||||
|
||||
return this.#gootiMetaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the service is not initialized.
|
||||
*/
|
||||
assureIsInitialized(): void {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error(
|
||||
'StorageService is not initialized. Please call "initialize(...)" before doing anything else.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async encrypt(value: string): Promise<string> {
|
||||
const browserSessionData =
|
||||
this.getBrowserSessionHandler().browserSessionData;
|
||||
if (!browserSessionData || !browserSessionData.vaultPassword) {
|
||||
throw new Error('Browser session data is undefined.');
|
||||
}
|
||||
|
||||
return CryptoHelper.encrypt(
|
||||
value,
|
||||
browserSessionData.iv,
|
||||
browserSessionData.vaultPassword
|
||||
);
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
value: string,
|
||||
returnType: 'string' | 'number' | 'boolean'
|
||||
): Promise<any> {
|
||||
const browserSessionData =
|
||||
this.getBrowserSessionHandler().browserSessionData;
|
||||
if (!browserSessionData || !browserSessionData.vaultPassword) {
|
||||
throw new Error('Browser session data is undefined.');
|
||||
}
|
||||
|
||||
return this.decryptWithLockedVault(
|
||||
value,
|
||||
returnType,
|
||||
browserSessionData.iv,
|
||||
browserSessionData.vaultPassword
|
||||
);
|
||||
}
|
||||
|
||||
async decryptWithLockedVault(
|
||||
value: string,
|
||||
returnType: 'string' | 'number' | 'boolean',
|
||||
iv: string,
|
||||
password: string
|
||||
): Promise<any> {
|
||||
const decryptedValue = await CryptoHelper.decrypt(value, iv, password);
|
||||
|
||||
switch (returnType) {
|
||||
case 'number':
|
||||
return parseInt(decryptedValue);
|
||||
|
||||
case 'boolean':
|
||||
return decryptedValue === 'true';
|
||||
|
||||
case 'string':
|
||||
default:
|
||||
return decryptedValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the browser sync data to the latest version.
|
||||
*/
|
||||
#migrateBrowserSyncData(browserSyncData: Partial<Record<string, any>>): {
|
||||
browserSyncData?: BrowserSyncData;
|
||||
migrationWasPerformed: boolean;
|
||||
} {
|
||||
if (Object.keys(browserSyncData).length === 0) {
|
||||
// First run. There is no browser sync data yet.
|
||||
return {
|
||||
browserSyncData: undefined,
|
||||
migrationWasPerformed: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Will be implemented if migration is required.
|
||||
return {
|
||||
browserSyncData: browserSyncData as BrowserSyncData,
|
||||
migrationWasPerformed: false,
|
||||
};
|
||||
}
|
||||
|
||||
#allegedBrowserSyncDataIsValid(data: BrowserSyncData): boolean {
|
||||
if (typeof data.iv === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.version !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.vaultHash === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.selectedIdentityId === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof data.identities === 'undefined' ||
|
||||
!Array.isArray(data.identities)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof data.permissions === 'undefined' ||
|
||||
!Array.isArray(data.permissions)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof data.relays === 'undefined' || !Array.isArray(data.relays)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
84
projects/common/src/lib/services/storage/types.ts
Normal file
84
projects/common/src/lib/services/storage/types.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Nip07Method, Nip07MethodPolicy } from '@common';
|
||||
|
||||
export interface Permission_DECRYPTED {
|
||||
id: string;
|
||||
identityId: string;
|
||||
host: string;
|
||||
method: Nip07Method;
|
||||
methodPolicy: Nip07MethodPolicy;
|
||||
kind?: number;
|
||||
}
|
||||
|
||||
export interface Permission_ENCRYPTED {
|
||||
id: string;
|
||||
identityId: string;
|
||||
host: string;
|
||||
method: string;
|
||||
methodPolicy: string;
|
||||
kind?: string;
|
||||
}
|
||||
|
||||
export interface Identity_DECRYPTED {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
nick: string;
|
||||
privkey: string;
|
||||
}
|
||||
|
||||
export type Identity_ENCRYPTED = Identity_DECRYPTED;
|
||||
|
||||
export interface Relay_DECRYPTED {
|
||||
id: string;
|
||||
identityId: string;
|
||||
url: string;
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
}
|
||||
|
||||
export interface Relay_ENCRYPTED {
|
||||
id: string;
|
||||
identityId: string;
|
||||
url: string;
|
||||
read: string;
|
||||
write: string;
|
||||
}
|
||||
|
||||
export interface BrowserSyncData_PART_Unencrypted {
|
||||
version: number;
|
||||
iv: string;
|
||||
vaultHash: string;
|
||||
}
|
||||
|
||||
export interface BrowserSyncData_PART_Encrypted {
|
||||
selectedIdentityId: string | null;
|
||||
permissions: Permission_ENCRYPTED[];
|
||||
identities: Identity_ENCRYPTED[];
|
||||
relays: Relay_ENCRYPTED[];
|
||||
}
|
||||
|
||||
export type BrowserSyncData = BrowserSyncData_PART_Unencrypted &
|
||||
BrowserSyncData_PART_Encrypted;
|
||||
|
||||
export enum BrowserSyncFlow {
|
||||
NO_SYNC = 0,
|
||||
BROWSER_SYNC = 1,
|
||||
GOOTI_SYNC = 2,
|
||||
CUSTOM_SYNC = 3,
|
||||
}
|
||||
|
||||
export interface BrowserSessionData {
|
||||
// The following properties purely come from the browser session storage
|
||||
// and will never be going into the browser sync storage.
|
||||
vaultPassword?: string;
|
||||
|
||||
// The following properties initially come from the browser sync storage.
|
||||
iv: string;
|
||||
permissions: Permission_DECRYPTED[];
|
||||
identities: Identity_DECRYPTED[];
|
||||
selectedIdentityId: string | null;
|
||||
relays: Relay_DECRYPTED[];
|
||||
}
|
||||
|
||||
export interface GootiMetaData {
|
||||
syncFlow?: number; // 0 = no sync, 1 = browser sync, (future: 2 = Gooti sync, 3 = Custom sync (bring your own sync))
|
||||
}
|
||||
Reference in New Issue
Block a user