1 import { getAliasOptions } from '@proton/pass/lib/alias/alias.requests';
2 import { exposeApi } from '@proton/pass/lib/api/api';
3 import { exposePassCrypto } from '@proton/pass/lib/crypto';
4 import { createPassCrypto } from '@proton/pass/lib/crypto/pass-crypto';
5 import { parseItemRevision } from '@proton/pass/lib/items/item.parser';
6 import { createAlias, requestAllItemsForShareId } from '@proton/pass/lib/items/item.requests';
7 import { getOrganizationSettings, setOrganizationSettings } from '@proton/pass/lib/organization/organization.requests';
8 import { parseShareResponse } from '@proton/pass/lib/shares/share.parser';
9 import { requestShares } from '@proton/pass/lib/shares/share.requests';
10 import { getUserAccess } from '@proton/pass/lib/user/user.requests';
11 import { isActiveVault, isOwnVault, isWritableVault } from '@proton/pass/lib/vaults/vault.predicates';
12 import { createVault } from '@proton/pass/lib/vaults/vault.requests';
13 import type { ItemRevision, Api as PassApi } from '@proton/pass/types';
14 import { first } from '@proton/pass/utils/array/first';
15 import { prop } from '@proton/pass/utils/fp/lens';
16 import { maxAgeMemoize } from '@proton/pass/utils/fp/memo';
17 import { pipe } from '@proton/pass/utils/fp/pipe';
18 import { and, truthy } from '@proton/pass/utils/fp/predicates';
19 import { sortOn } from '@proton/pass/utils/fp/sort';
20 import { waitUntil } from '@proton/pass/utils/fp/wait-until';
21 import { obfuscate } from '@proton/pass/utils/obfuscate/xor';
22 import { uniqueId } from '@proton/pass/utils/string/unique-id';
23 import { getEpoch } from '@proton/pass/utils/time/epoch';
24 import type { Api } from '@proton/shared/lib/interfaces';
25 import unary from '@proton/utils/unary';
27 import type { PassBridge, PassBridgeAliasItem } from './types';
29 let passBridgeInstance: PassBridge | undefined;
31 export const createPassBridge = (api: Api): PassBridge => {
35 exposeApi(api as PassApi);
36 const PassCrypto = exposePassCrypto(createPassCrypto());
38 passBridgeInstance = {
39 async init({ user, addresses, authStore }) {
40 await PassCrypto.hydrate({ user, addresses, keyPassword: authStore.getPassword(), clear: false });
41 const isReady = await waitUntil(() => PassCrypto.ready, 250).then(() => true);
46 getUserAccess: maxAgeMemoize(async () => {
47 const result = await getUserAccess();
52 getDefault: maxAgeMemoize(async () => {
53 const encryptedShares = await requestShares();
54 const shares = (await Promise.all(encryptedShares.map(unary(parseShareResponse)))).filter(
58 const candidates = shares
59 .filter(and(isActiveVault, isWritableVault, isOwnVault))
60 .sort(sortOn('createTime', 'ASC'));
62 return first(candidates);
64 async createDefaultVault() {
65 // In case a default vault has been created in the meantime
66 const defaultVault = await this.getDefault({ maxAge: 0 });
71 const newVault = await createVault({
74 description: 'Personal vault (created from Mail)',
82 async create({ shareId, name, note, alias: { aliasEmail, mailbox, prefix, signedSuffix } }) {
83 const itemUuid = uniqueId();
85 const encryptedItem = await createAlias({
87 createTime: getEpoch(),
88 extraData: { aliasEmail, mailboxes: [mailbox], prefix, signedSuffix },
90 metadata: { itemUuid, name, note: obfuscate(note ?? '') },
91 optimisticId: itemUuid,
96 const item = (await parseItemRevision(shareId, encryptedItem)) as ItemRevision<'alias'>;
99 item: { ...item, aliasEmail },
103 getAllByShareId: maxAgeMemoize(async (shareId) => {
104 const aliases = (await Promise.all(
105 (await requestAllItemsForShareId({ shareId, OnlyAlias: true }))
106 .filter(pipe(prop('AliasEmail'), truthy))
107 .map((item) => parseItemRevision(shareId, item))
108 )) as ItemRevision<'alias'>[];
110 return aliases.map((item): PassBridgeAliasItem => ({ item }));
115 get: getOrganizationSettings,
116 set: (key, value) => setOrganizationSettings({ [key]: value }),
121 return passBridgeInstance;