Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / author.ts
blob1e43e35c71a5fc9b2385f06593318fb6f41df0ac
1 import type { PublicKeyReference } from '@proton/crypto';
2 import { captureMessage } from '@proton/shared/lib/helpers/sentry';
3 import type { ContactEmail } from '@proton/shared/lib/interfaces/contacts';
4 import type { GetVerificationPreferences } from '@proton/shared/lib/interfaces/hooks/GetVerificationPreferences';
5 import isTruthy from '@proton/utils/isTruthy';
6 import unique from '@proton/utils/unique';
8 import { canonicalizeInternalEmail } from '../helpers/email';
9 import type { Address } from '../interfaces';
10 import type { CalendarEvent, CalendarEventData } from '../interfaces/calendar';
11 import type { GetAddressKeys } from '../interfaces/hooks/GetAddressKeys';
12 import type { SimpleMap } from '../interfaces/utils';
13 import { getKeyHasFlagsToVerify } from '../keys';
14 import { getActiveKeys } from '../keys/getActiveKeys';
15 import { CALENDAR_CARD_TYPE } from './constants';
17 const { SIGNED, ENCRYPTED_AND_SIGNED } = CALENDAR_CARD_TYPE;
19 export const withNormalizedAuthor = (x: CalendarEventData) => ({
20     ...x,
21     Author: canonicalizeInternalEmail(x.Author),
22 });
23 export const withNormalizedAuthors = (x: CalendarEventData[]) => {
24     if (!x) {
25         return [];
26     }
27     return x.map(withNormalizedAuthor);
30 interface GetAuthorPublicKeysMap {
31     event: CalendarEvent;
32     addresses: Address[];
33     getAddressKeys: GetAddressKeys;
34     getVerificationPreferences: GetVerificationPreferences;
35     contactEmailsMap: SimpleMap<ContactEmail>;
38 export const getAuthorPublicKeysMap = async ({
39     event,
40     addresses,
41     getAddressKeys,
42     getVerificationPreferences,
43     contactEmailsMap,
44 }: GetAuthorPublicKeysMap) => {
45     const publicKeysMap: SimpleMap<PublicKeyReference | PublicKeyReference[]> = {};
46     const authors = unique(
47         [...event.SharedEvents, ...event.CalendarEvents]
48             .map(({ Author, Type }) => {
49                 if (![SIGNED, ENCRYPTED_AND_SIGNED].includes(Type)) {
50                     // no need to fetch keys in this case
51                     return;
52                 }
53                 return canonicalizeInternalEmail(Author);
54             })
55             .filter(isTruthy)
56     );
57     const normalizedAddresses = addresses.map((address) => ({
58         ...address,
59         normalizedEmailAddress: canonicalizeInternalEmail(address.Email),
60     }));
61     const promises = authors.map(async (author) => {
62         const ownAddress = normalizedAddresses.find(({ normalizedEmailAddress }) => normalizedEmailAddress === author);
63         if (ownAddress) {
64             const decryptedKeys = await getAddressKeys(ownAddress.ID);
65             const addressKeys = await getActiveKeys(
66                 ownAddress,
67                 ownAddress.SignedKeyList,
68                 ownAddress.Keys,
69                 decryptedKeys
70             );
71             publicKeysMap[author] = addressKeys
72                 .filter((decryptedKey) => {
73                     return getKeyHasFlagsToVerify(decryptedKey.flags);
74                 })
75                 .map((key) => key.publicKey);
76         } else {
77             try {
78                 const { verifyingKeys } = await getVerificationPreferences({ email: author, contactEmailsMap });
79                 publicKeysMap[author] = verifyingKeys;
80             } catch (error: any) {
81                 // We're seeing too many unexpected offline errors in the GET /keys route.
82                 // We log them to Sentry and ignore them here (no verification will take place in these cases)
83                 const { ID, CalendarID } = event;
84                 const errorMessage = error?.message || 'Unknown error';
85                 captureMessage('Unexpected error verifying event signature', {
86                     extra: { message: errorMessage, eventID: ID, calendarID: CalendarID },
87                 });
88             }
89         }
90     });
91     await Promise.all(promises);
93     return publicKeysMap;