aboutsummaryrefslogtreecommitdiffstats
path: root/packages/renderer/src/components/errorPage/CertificateDetails.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/renderer/src/components/errorPage/CertificateDetails.tsx')
-rw-r--r--packages/renderer/src/components/errorPage/CertificateDetails.tsx136
1 files changed, 115 insertions, 21 deletions
diff --git a/packages/renderer/src/components/errorPage/CertificateDetails.tsx b/packages/renderer/src/components/errorPage/CertificateDetails.tsx
index 044cb5c..51e7920 100644
--- a/packages/renderer/src/components/errorPage/CertificateDetails.tsx
+++ b/packages/renderer/src/components/errorPage/CertificateDetails.tsx
@@ -19,22 +19,41 @@
19 */ 19 */
20 20
21import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; 21import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
22import WarningAmberIcon from '@mui/icons-material/WarningAmber';
23import Accordion from '@mui/material/Accordion'; 22import Accordion from '@mui/material/Accordion';
24import AccordionDetails from '@mui/material/AccordionDetails'; 23import AccordionDetails from '@mui/material/AccordionDetails';
25import AccordionSummary from '@mui/material/AccordionSummary'; 24import AccordionSummary from '@mui/material/AccordionSummary';
26import Box from '@mui/material/Box'; 25import Box from '@mui/material/Box';
27import Button from '@mui/material/Button'; 26import Tab from '@mui/material/Tab';
27import Tabs from '@mui/material/Tabs';
28import Typography from '@mui/material/Typography'; 28import Typography from '@mui/material/Typography';
29import { styled } from '@mui/material/styles';
30import type { Certificate } from '@sophie/shared';
29import { observer } from 'mobx-react-lite'; 31import { observer } from 'mobx-react-lite';
30import React from 'react'; 32import React, { useState } from 'react';
31import { useTranslation } from 'react-i18next'; 33import { useTranslation } from 'react-i18next';
32 34
33import type Service from '../../stores/Service'; 35import type Service from '../../stores/Service';
34 36
37import SingleCertificateDetails from './SingleCertificateDetails';
38import TrustCertificateDialog from './TrustCertificateDialog';
39
35const SUMMARY_ID = 'Sophie-CertificateDetails-header'; 40const SUMMARY_ID = 'Sophie-CertificateDetails-header';
36const DETAILS_ID = 'Sophie-CertificateDetails-content'; 41const DETAILS_ID = 'Sophie-CertificateDetails-content';
37 42
43function getCertificateChain(endEntityCertificate: Certificate): Certificate[] {
44 const chain: Certificate[] = [];
45 let certificate: Certificate | undefined = endEntityCertificate;
46 while (certificate !== undefined) {
47 chain.push(certificate);
48 certificate = certificate.issuerCert as Certificate;
49 }
50 return chain;
51}
52
53const DetailsBox = styled(Box)(({ theme }) => ({
54 padding: `0 ${theme.spacing(2)}`,
55}));
56
38function CertificateDetails({ 57function CertificateDetails({
39 service, 58 service,
40}: { 59}: {
@@ -43,8 +62,9 @@ function CertificateDetails({
43 const { t } = useTranslation(undefined, { 62 const { t } = useTranslation(undefined, {
44 keyPrefix: 'error.certificateError.details', 63 keyPrefix: 'error.certificateError.details',
45 }); 64 });
65 const [certificateIndex, setCertificateIndex] = useState(0);
46 66
47 const { state } = service; 67 const { id: serviceId, state } = service;
48 const { type: errorType } = state; 68 const { type: errorType } = state;
49 69
50 if (errorType !== 'certificateError') { 70 if (errorType !== 'certificateError') {
@@ -54,6 +74,22 @@ function CertificateDetails({
54 74
55 const { certificate, trust } = state; 75 const { certificate, trust } = state;
56 76
77 const chain = getCertificateChain(certificate);
78
79 if (chain.length === 0) {
80 // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering.
81 return null;
82 }
83
84 const id = `${serviceId}-certificateDetails`;
85
86 const trustCertificateDialog = (
87 <TrustCertificateDialog
88 trust={trust}
89 onClick={() => service.temporarilyTrustCurrentCertificate()}
90 />
91 );
92
57 return ( 93 return (
58 <Accordion 94 <Accordion
59 disableGutters 95 disableGutters
@@ -67,23 +103,81 @@ function CertificateDetails({
67 > 103 >
68 <Typography>{t('title')}</Typography> 104 <Typography>{t('title')}</Typography>
69 </AccordionSummary> 105 </AccordionSummary>
70 <AccordionDetails> 106 <AccordionDetails
71 <Typography sx={{ wordWrap: 'break-word' }}> 107 sx={{
72 {JSON.stringify(certificate)} 108 px: 0,
73 </Typography> 109 pt: 0,
74 <Typography mt={2}> 110 }}
75 <WarningAmberIcon fontSize="small" /> <strong>{t('warning')}</strong> 111 >
76 </Typography> 112 {chain.length >= 2 ? (
77 <Box mt={1}> 113 <>
78 <Button 114 <Tabs
79 disabled={trust !== 'pending'} 115 aria-label={t<string>('tabsLabel')}
80 onClick={() => service.temporarilyTrustCurrentCertificate()} 116 value={certificateIndex < chain.length ? certificateIndex : false}
81 color="error" 117 onChange={(_event, value) => setCertificateIndex(value as number)}
82 variant="outlined" 118 variant="fullWidth"
83 > 119 >
84 {t(`acceptButton.${trust}`)} 120 {chain.map((member, i) => (
85 </Button> 121 <Tab
86 </Box> 122 key={member.fingerprint}
123 value={i}
124 id={`${id}-tab-${i}`}
125 aria-controls={`${id}-tabpanel-${i}`}
126 label={
127 member.subjectName === ''
128 ? member.fingerprint
129 : member.subjectName
130 }
131 />
132 ))}
133 </Tabs>
134 <DetailsBox
135 sx={(theme) => ({
136 paddingTop: 1,
137 borderTop: `1px solid ${theme.palette.divider}`,
138 })}
139 >
140 {chain.map((member, i) => (
141 <div
142 key={member.fingerprint}
143 role="tabpanel"
144 hidden={certificateIndex !== i}
145 id={`${id}-tabpanel-${i}`}
146 aria-labelledby={`${id}-tab-${i}`}
147 >
148 {certificateIndex === i && (
149 <>
150 <SingleCertificateDetails
151 detailsId={id}
152 certificate={member}
153 onIssuerClick={
154 i < chain.length - 1
155 ? () => setCertificateIndex(i + 1)
156 : undefined
157 }
158 onDownloadClick={() =>
159 service.downloadCertificate(member.fingerprint)
160 }
161 />
162 {i === 0 && trustCertificateDialog}
163 </>
164 )}
165 </div>
166 ))}
167 </DetailsBox>
168 </>
169 ) : (
170 <DetailsBox>
171 <SingleCertificateDetails
172 detailsId={id}
173 certificate={chain[0]}
174 onDownloadClick={() =>
175 service.downloadCertificate(chain[0].fingerprint)
176 }
177 />
178 {trustCertificateDialog}
179 </DetailsBox>
180 )}
87 </AccordionDetails> 181 </AccordionDetails>
88 </Accordion> 182 </Accordion>
89 ); 183 );