Skip to content

Commit 8e082e7

Browse files
committed
feat: display more informatin about certificate
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent b5b89f4 commit 8e082e7

1 file changed

Lines changed: 226 additions & 70 deletions

File tree

src/views/ReadCertificate/CertificateContent.vue

Lines changed: 226 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,119 @@
33
- SPDX-License-Identifier: AGPL-3.0-or-later
44
-->
55
<template>
6-
<table v-if="Object.keys(certificate).length">
7-
<tr>
8-
<th colspan="2">
9-
{{ t('libresign', 'Owner of certificate') }}
10-
</th>
11-
</tr>
12-
<tr v-for="(value, customName) in orderList(certificate.subject)" :key="customName">
13-
<td>{{ getLabelFromId(customName) }}</td>
14-
<td>{{ Array.isArray(value) ? value.join(', ') : value }}</td>
15-
</tr>
16-
<tr v-if="index !== 0">
17-
<th colspan="2">
18-
{{ t('libresign', 'Issuer of certificate') }}
19-
</th>
20-
</tr>
21-
<tr v-for="(value, customName) in orderList(certificate.issuer)" :key="value">
22-
<td>{{ getLabelFromId(customName) }}</td>
23-
<td>{{ value }}</td>
24-
</tr>
25-
<tr v-if="certificate.extracerts">
26-
<th colspan="2">
27-
{{ t('libresign', 'Certificate chain:') }}
28-
</th>
29-
</tr>
30-
<tr v-for="(extra, key) in certificate.extracerts"
31-
:key="`extracerts-${key}`"
32-
class="certificate-chain">
33-
<td>
34-
{{ key }}
35-
</td>
36-
<td>
37-
<CertificateContent :certificate="extra" :index="index + '_' + key" />
38-
</td>
39-
</tr>
40-
<tr>
41-
<td>{{ t('libresign', 'Certificate valid from:') }}</td>
42-
<td>{{ certificate.valid_from }}</td>
43-
</tr>
44-
<tr>
45-
<td>{{ t('libresign', 'Certificate valid to:') }}</td>
46-
<td>{{ certificate.valid_to }}</td>
47-
</tr>
48-
<tr>
49-
<th colspan="2">
50-
{{ t('libresign', 'Extra information') }}
51-
</th>
52-
</tr>
53-
<tr>
54-
<td>Name</td>
55-
<td>{{ certificate.name }}</td>
56-
</tr>
57-
<tr v-for="(value, name) in certificate.extensions" :key="name">
58-
<td>{{ name }}</td>
59-
<td>{{ value }}</td>
60-
</tr>
61-
</table>
6+
<div v-if="Object.keys(certificate).length" class="certificate-content">
7+
<!-- Owner Section -->
8+
<NcSettingsSection :name="t('libresign', 'Owner of certificate')">
9+
<div class="certificate-fields">
10+
<div v-for="(value, customName) in orderList(certificate.subject)"
11+
:key="customName"
12+
class="certificate-field">
13+
<span class="field-label">{{ getLabelFromId(customName) }}</span>
14+
<span class="field-value">{{ Array.isArray(value) ? value.join(', ') : value }}</span>
15+
</div>
16+
</div>
17+
</NcSettingsSection>
18+
19+
<NcSettingsSection v-if="index !== 0"
20+
:name="t('libresign', 'Issuer of certificate')">
21+
<div class="certificate-fields">
22+
<div v-for="(value, customName) in orderList(certificate.issuer)"
23+
:key="value"
24+
class="certificate-field">
25+
<span class="field-label">{{ getLabelFromId(customName) }}</span>
26+
<span class="field-value">{{ value }}</span>
27+
</div>
28+
</div>
29+
</NcSettingsSection>
30+
31+
<NcSettingsSection v-if="certificate.extracerts && index === '0'"
32+
:name="t('libresign', 'Certificate chain')">
33+
<div class="certificate-chain-container">
34+
<div v-for="(extra, key) in certificate.extracerts"
35+
:key="`extracerts-${key}`"
36+
class="chain-certificate">
37+
<h4 class="chain-title">
38+
{{ getChainCertificateLabel(key, extra) }}
39+
</h4>
40+
<CertificateContent :certificate="extra" :index="index + '_' + key" />
41+
</div>
42+
</div>
43+
</NcSettingsSection>
44+
45+
<NcSettingsSection :name="t('libresign', 'Certificate Information')">
46+
<div class="certificate-fields">
47+
<div v-if="index === '0'" class="certificate-field">
48+
<span class="field-label">{{ t('libresign', 'Valid from') }}</span>
49+
<span class="field-value">{{ certificate.valid_from }}</span>
50+
</div>
51+
<div v-if="index === '0'" class="certificate-field">
52+
<span class="field-label">{{ t('libresign', 'Valid to') }}</span>
53+
<span class="field-value">{{ certificate.valid_to }}</span>
54+
</div>
55+
<div v-if="certificate.version !== undefined" class="certificate-field">
56+
<span class="field-label">{{ t('libresign', 'Version') }}</span>
57+
<span class="field-value">{{ certificate.version + 1 }}</span>
58+
</div>
59+
<div v-if="certificate.hash" class="certificate-field">
60+
<span class="field-label">{{ t('libresign', 'Fingerprint') }}</span>
61+
<span class="field-value">{{ certificate.hash }}</span>
62+
</div>
63+
<div v-if="certificate.signatureTypeLN" class="certificate-field">
64+
<span class="field-label">{{ t('libresign', 'Signature algorithm') }}</span>
65+
<span class="field-value">{{ certificate.signatureTypeLN }}</span>
66+
</div>
67+
<div v-if="certificate.serialNumber" class="certificate-field">
68+
<span class="field-label">{{ t('libresign', 'Serial number') }}</span>
69+
<span class="field-value">{{ certificate.serialNumber }}</span>
70+
</div>
71+
</div>
72+
</NcSettingsSection>
73+
74+
<NcSettingsSection v-if="index === '0'"
75+
:name="t('libresign', 'Technical Details')">
76+
<div class="certificate-fields">
77+
<div class="certificate-field">
78+
<span class="field-label">Name</span>
79+
<span class="field-value">{{ certificate.name }}</span>
80+
</div>
81+
<div v-for="(value, name) in certificate.extensions"
82+
:key="name"
83+
class="certificate-field">
84+
<span class="field-label">{{ name }}</span>
85+
<span class="field-value">{{ value }}</span>
86+
</div>
87+
</div>
88+
</NcSettingsSection>
89+
90+
<NcSettingsSection v-if="certificate.purposes && Object.keys(certificate.purposes).length && index === '0'"
91+
:name="t('libresign', 'Certificate purposes')">
92+
<div class="purposes-grid">
93+
<div v-for="(purpose, purposeIndex) in certificate.purposes"
94+
:key="purposeIndex"
95+
class="purpose-item"
96+
:class="{ 'purpose-allowed': purpose[0], 'purpose-denied': !purpose[0] }">
97+
<div class="purpose-name">{{ formatPurposeName(purpose[2]) }}</div>
98+
<div class="purpose-status">
99+
<span v-if="purpose[0]" class="status-allowed">✓ {{ t('libresign', 'Allowed') }}</span>
100+
<span v-else class="status-denied">✗ {{ t('libresign', 'Not allowed') }}</span>
101+
<span v-if="purpose[1]" class="ca-badge">CA</span>
102+
</div>
103+
</div>
104+
</div>
105+
</NcSettingsSection>
106+
</div>
62107
</template>
63108

64109
<script>
65110
66-
// import CertificateContent from './CertificateContent.vue'
67111
import { selectCustonOption } from '../../helpers/certification.js'
112+
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
68113
69114
export default {
70115
name: 'CertificateContent',
116+
components: {
117+
NcSettingsSection,
118+
},
71119
props: {
72120
certificate: {
73121
type: Object,
@@ -79,6 +127,9 @@ export default {
79127
default: '0',
80128
},
81129
},
130+
data() {
131+
return {}
132+
},
82133
methods: {
83134
orderList(data) {
84135
const sorted = {};
@@ -102,34 +153,139 @@ export default {
102153
return id
103154
}
104155
},
156+
formatPurposeName(purpose) {
157+
const purposeNames = {
158+
'sslclient': this.t('libresign', 'SSL Client'),
159+
'sslserver': this.t('libresign', 'SSL Server'),
160+
'nssslserver': this.t('libresign', 'Netscape SSL Server'),
161+
'smimesign': this.t('libresign', 'S/MIME Signing'),
162+
'smimeencrypt': this.t('libresign', 'S/MIME Encryption'),
163+
'crlsign': this.t('libresign', 'CRL Signing'),
164+
'any': this.t('libresign', 'Any Purpose'),
165+
'ocsphelper': this.t('libresign', 'OCSP Helper'),
166+
'timestampsign': this.t('libresign', 'Timestamp Signing'),
167+
'codesign': this.t('libresign', 'Code Signing'),
168+
}
169+
return purposeNames[purpose] || purpose
170+
},
171+
getChainCertificateLabel(index, certificate) {
172+
if (index === 0) {
173+
return this.t('libresign', 'Intermediate Certificate')
174+
}
175+
if (certificate.subject && certificate.issuer &&
176+
JSON.stringify(certificate.subject) === JSON.stringify(certificate.issuer)) {
177+
return this.t('libresign', 'Root Certificate (CA)')
178+
}
179+
return this.t('libresign', 'Certificate {number}', { number: index + 1 })
180+
},
105181
},
106182
}
107183
</script>
184+
108185
<style lang="scss" scoped>
109-
table {
110-
width: 100%;
111-
white-space: unset;
186+
.certificate-content {
187+
max-width: 900px;
188+
}
189+
190+
.certificate-field {
191+
display: flex;
192+
flex-direction: column;
193+
padding: 12px 0;
194+
border-bottom: 1px solid var(--color-border-dark);
195+
gap: 4px;
196+
197+
&:last-child {
198+
border-bottom: none;
199+
}
200+
201+
@media (min-width: 768px) {
202+
flex-direction: row;
203+
gap: 16px;
204+
}
112205
}
113206
114-
td {
115-
padding: 5px;
116-
border-bottom: 1px solid var(--color-border);
207+
.field-label {
208+
font-size: 12px;
209+
color: var(--color-text-maxcontrast);
210+
font-weight: 500;
211+
212+
@media (min-width: 768px) {
213+
min-width: 140px;
214+
text-align: right;
215+
padding-right: 16px;
216+
border-right: 1px solid var(--color-border-dark);
217+
}
117218
}
118219
119-
td:nth-child(2) {
220+
.field-value {
221+
color: var(--color-main-text);
120222
word-break: break-all;
223+
flex: 1;
224+
}
225+
226+
.chain-certificate {
227+
background: var(--color-background-hover);
228+
border-radius: var(--border-radius);
229+
margin-bottom: 16px;
230+
231+
.chain-title {
232+
background: var(--color-background-dark);
233+
padding: 12px 16px;
234+
font-weight: 600;
235+
border-radius: var(--border-radius) var(--border-radius) 0 0;
236+
}
237+
238+
.certificate-content {
239+
padding: 16px;
240+
}
241+
}
242+
243+
.purposes-grid {
244+
display: grid;
245+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
246+
gap: 12px;
121247
}
122248
123-
th {
249+
.purpose-item {
250+
background: var(--color-background-hover);
251+
border: 1px solid var(--color-border);
252+
border-radius: var(--border-radius);
253+
padding: 12px;
254+
255+
&.purpose-allowed {
256+
border-left: 4px solid var(--color-success);
257+
}
258+
259+
&.purpose-denied {
260+
border-left: 4px solid var(--color-error);
261+
}
262+
}
263+
264+
.purpose-status {
265+
display: flex;
266+
align-items: center;
267+
gap: 8px;
268+
font-size: 12px;
269+
margin-top: 4px;
270+
}
271+
272+
.status-allowed {
273+
color: var(--color-success);
124274
font-weight: bold;
125275
}
126276
127-
tr:last-child td {
128-
border-bottom: none;
277+
.status-denied {
278+
color: var(--color-error);
279+
font-weight: bold;
129280
}
130281
131-
td:first-child, th:first-child {
132-
opacity: .5;
133-
word-break: normal;
282+
.ca-badge {
283+
background: var(--color-warning);
284+
color: white;
285+
padding: 2px 6px;
286+
border-radius: 10px;
287+
font-size: 10px;
288+
font-weight: bold;
289+
text-transform: uppercase;
134290
}
135291
</style>

0 commit comments

Comments
 (0)