Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / drive-store / components / SignatureAlert.tsx
blob5c204cbd83b2dad0bd500cb8ea025732786489bb
1 import { c } from 'ttag';
3 import { Alert, TextLoader } from '@proton/components';
4 import { VERIFICATION_STATUS } from '@proton/crypto';
5 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
7 import type { SignatureIssueLocation, SignatureIssues } from '../store';
8 import { hasValidAnonymousSignature } from './hasValidAnonymousSignature';
10 type Props = {
11     loading: boolean;
12     signatureIssues: SignatureIssues | undefined;
13     signatureNetworkError?: boolean;
14     signatureAddress: string | undefined;
15     isFile: boolean;
16     name: string;
17     corruptedLink?: boolean;
18     className?: string;
21 export default function SignatureAlert({
22     loading,
23     signatureIssues,
24     signatureAddress,
25     signatureNetworkError,
26     corruptedLink,
27     className,
28     ...props
29 }: Props) {
30     if (loading) {
31         return (
32             <Alert type="info" className={className}>
33                 <TextLoader className="m-0">{c('Info').t`Checking signatures`}</TextLoader>
34             </Alert>
35         );
36     }
38     if (corruptedLink) {
39         return (
40             <Alert type="warning" className={className}>
41                 <span>{c('Info')
42                     .t`Unfortunately, it appears that the file or some of its data cannot be decrypted.`}</span>
43             </Alert>
44         );
45     }
47     if (signatureNetworkError) {
48         return (
49             <Alert type="warning" className={className}>
50                 <span>{c('Info').t`Signature cannot be validated due to network error, please try again later.`}</span>
51             </Alert>
52         );
53     }
55     const validAnonymousSignature =
56         !!signatureIssues && !signatureAddress && hasValidAnonymousSignature(signatureIssues);
58     return (
59         <Alert type={signatureIssues && !validAnonymousSignature ? 'error' : 'success'} className={className}>
60             <SignatureAlertBody signatureIssues={signatureIssues} signatureAddress={signatureAddress} {...props} />
61         </Alert>
62     );
65 type PropsBody = {
66     signatureIssues: SignatureIssues | undefined;
67     signatureAddress: string | undefined;
68     isFile: boolean;
69     name: string;
72 export function SignatureAlertBody({ signatureIssues, signatureAddress, isFile, name }: PropsBody) {
73     const fileName = (
74         <strong className="text-break" key="fileName">
75             {name}
76         </strong>
77     );
79     const emailAddress = (
80         <strong className="text-break" key="signatureAddress" data-testid="signature-address">
81             {signatureAddress || c('Info').t`an anonymous user`}
82         </strong>
83     );
85     if (!signatureIssues) {
86         return (
87             <>
88                 {isFile
89                     ? c('Info').jt`Digital signature verified. This file was securely uploaded by ${emailAddress}.`
90                     : c('Info').jt`Digital signature verified. This folder was securely uploaded by ${emailAddress}.`}
91             </>
92         );
93     }
95     if (!signatureAddress && hasValidAnonymousSignature(signatureIssues)) {
96         return (
97             <>
98                 {isFile
99                     ? c('Info')
100                           .jt`The digital signature has been partially verified. The file was uploaded from a public page that does not specify a specific user origin.`
101                     : c('Info')
102                           .jt`The digital signature has been partially verified. The folder was uploaded from a public page that does not specify a specific user origin.`}
103             </>
104         );
105     }
107     const locationTranslations: { [key in SignatureIssueLocation]: string } = {
108         passphrase: c('Item').t`keys`,
109         hash: c('Item').t`hash key`,
110         name: c('Item').t`name`,
111         xattrs: c('Item').t`file attributes`,
112         contentKeyPacket: c('Item').t`file data key`,
113         blocks: c('Item').t`file data`,
114         thumbnail: c('Item').t`thumbnail`,
115         manifest: c('Item').t`file data order`,
116     };
117     const items = Object.keys(signatureIssues)
118         .map((location) => locationTranslations[location as SignatureIssueLocation])
119         .join(', ');
121     const statuses = Object.values(signatureIssues);
122     const hasNoSignatureIssue = statuses.some((status) => status === VERIFICATION_STATUS.NOT_SIGNED);
123     const hasBadSignatureIssue = statuses.some((status) => status === VERIFICATION_STATUS.SIGNED_AND_INVALID);
125     let textReason;
126     let textWarning;
127     if (hasNoSignatureIssue && !hasBadSignatureIssue) {
128         if (signatureAddress) {
129             textReason = isFile
130                 ? c('Info').jt`File is missing signature. We couldn’t verify that ${emailAddress} uploaded ${fileName}.`
131                 : c('Info')
132                       .jt`Folder is missing signature. We couldn’t verify that ${emailAddress} uploaded ${fileName}.`;
133         } else {
134             textReason = isFile
135                 ? c('Info').jt`File is missing signature. We couldn’t verify who uploaded ${fileName}.`
136                 : c('Info').jt`Folder is missing signature. We couldn’t verify who uploaded ${fileName}.`;
137         }
138         textWarning = c('Info').jt`The following may have been tampered with: ${items}. Only open if you trust it.`;
139     } else {
140         if (signatureAddress) {
141             textReason = c('Info').jt`We couldn’t verify that ${emailAddress} uploaded ${fileName}.`;
142             textWarning = c('Info')
143                 .jt`The account may have a new key, or the following may have been tampered with: ${items}. Only open if you trust it.`;
144         } else {
145             textReason = c('Info').jt`We couldn’t verify who uploaded ${fileName}.`;
146             textWarning = c('Info').jt`The following may have been tampered with: ${items}. Only open if you trust it.`;
147         }
148     }
150     return (
151         <>
152             {textReason}
153             &nbsp;
154             {textWarning}
155             &nbsp;
156             <a href={getKnowledgeBaseUrl('/drive-signature-management')} target="_blank">
157                 {c('Action').t`Learn more`}
158             </a>
159         </>
160     );