Skip to content

Commit c664e86

Browse files
committed
Add storing for Client CA certificates in the database
Add initial support for managing Client Certificate Authority public certificates as certificate objects in the database. The new provider type 'clientca' is defined to implement this.
1 parent b19a272 commit c664e86

File tree

6 files changed

+58
-16
lines changed

6 files changed

+58
-16
lines changed

backend/internal/certificate.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -552,13 +552,18 @@ const internalCertificate = {
552552
})
553553
.then(() => {
554554
return new Promise((resolve, reject) => {
555-
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
556-
if (err) {
557-
reject(err);
558-
} else {
559-
resolve();
560-
}
561-
});
555+
if (certificate.provider === 'clientca') {
556+
// Client CAs have no private key associated, so just succeed.
557+
resolve();
558+
} else {
559+
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
560+
if (err) {
561+
reject(err);
562+
} else {
563+
resolve();
564+
}
565+
});
566+
}
562567
});
563568
});
564569
},
@@ -639,7 +644,7 @@ const internalCertificate = {
639644
upload: (access, data) => {
640645
return internalCertificate.get(access, {id: data.id})
641646
.then((row) => {
642-
if (row.provider !== 'other') {
647+
if (row.provider !== 'other' && row.provider !== 'clientca') {
643648
throw new error.ValidationError('Cannot upload certificates for this type of provider');
644649
}
645650

backend/schema/definitions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@
219219
},
220220
"ssl_provider": {
221221
"type": "string",
222-
"pattern": "^(letsencrypt|other)$"
222+
"pattern": "^(letsencrypt|other|clientca)$"
223223
},
224224
"http2_support": {
225225
"description": "HTTP2 Protocol Support",

frontend/js/app/nginx/certificates/form.ejs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,23 @@
173173
</div>
174174
</div>
175175
</div>
176-
176+
<% } else if (provider === 'clientca') { %>
177+
<!-- Client Certificate Authority -->
178+
<div class="col-sm-12 col-md-12">
179+
<div class="form-group">
180+
<label class="form-label"><%- i18n('str', 'name') %> <span class="form-required">*</span></label>
181+
<input name="nice_name" type="text" class="form-control" placeholder="" value="<%- nice_name %>" required>
182+
</div>
183+
</div>
184+
<div class="col-sm-12 col-md-12 other-ssl">
185+
<div class="form-group">
186+
<div class="form-label"><%- i18n('certificates', 'clientca-certificate') %><span class="form-required">*</span></div>
187+
<div class="custom-file">
188+
<input type="file" class="custom-file-input" name="meta[clientca_certificate]" id="clientca_certificate">
189+
<label id="clientca_certificate_label" class="custom-file-label"><%- i18n('str', 'choose-file') %></label>
190+
</div>
191+
</div>
192+
</div>
177193
<% } %>
178194
</div>
179195
</form>

frontend/js/app/nginx/certificates/form.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ module.exports = Mn.View.extend({
4545
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
4646
other_certificate_key_label: '#other_certificate_key_label',
4747
other_intermediate_certificate: '#other_intermediate_certificate',
48-
other_intermediate_certificate_label: '#other_intermediate_certificate_label'
48+
other_intermediate_certificate_label: '#other_intermediate_certificate_label',
49+
clientca_certificate: '#clientca_certificate',
50+
clientca_certificate_label: '#clientca_certificate_label'
4951
},
5052

5153
events: {
@@ -156,21 +158,33 @@ module.exports = Mn.View.extend({
156158
}
157159
ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});
158160
}
161+
} else if (data.provider === 'clientca' && !this.model.hasSslFiles()) {
162+
// check files are attached
163+
if (!this.ui.clientca_certificate[0].files.length || !this.ui.clientca_certificate[0].files[0].size) {
164+
alert('Certificate file is not attached');
165+
return;
166+
} else {
167+
if (this.ui.clientca_certificate[0].files[0].size > this.max_file_size) {
168+
alert('Certificate file is too large (> 100kb)');
169+
return;
170+
}
171+
ssl_files.push({name: 'certificate', file: this.ui.clientca_certificate[0].files[0]});
172+
}
159173
}
160174

161175
this.ui.loader_content.show();
162176
this.ui.non_loader_content.hide();
163177

164178
// compile file data
165179
let form_data = new FormData();
166-
if (data.provider === 'other' && ssl_files.length) {
180+
if ((data.provider === 'other' || data.provider === 'clientca') && ssl_files.length) {
167181
ssl_files.map(function (file) {
168182
form_data.append(file.name, file.file);
169183
});
170184
}
171185

172186
new Promise(resolve => {
173-
if (data.provider === 'other') {
187+
if (data.provider === 'other' || data.provider === 'clientca') {
174188
resolve(App.Api.Nginx.Certificates.validate(form_data));
175189
} else {
176190
resolve();
@@ -183,7 +197,7 @@ module.exports = Mn.View.extend({
183197
this.model.set(result);
184198

185199
// Now upload the certs if we need to
186-
if (data.provider === 'other') {
200+
if (data.provider === 'other' || data.provider === 'clientca') {
187201
return App.Api.Nginx.Certificates.upload(this.model.get('id'), form_data)
188202
.then(result => {
189203
this.model.set('meta', _.assign({}, this.model.get('meta'), result));
@@ -234,6 +248,9 @@ module.exports = Mn.View.extend({
234248
},
235249
'change @ui.other_intermediate_certificate': function(e){
236250
this.setFileName("other_intermediate_certificate_label", e)
251+
},
252+
'change @ui.clientca_certificate': function(e){
253+
this.setFileName("clientca_certificate_label", e)
237254
}
238255
},
239256
setFileName(ui, e){

frontend/js/app/nginx/certificates/main.ejs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<div class="dropdown-menu">
2121
<a class="dropdown-item add-item" data-cert="letsencrypt" href="#"><%- i18n('ssl', 'letsencrypt') %></a>
2222
<a class="dropdown-item add-item" data-cert="other" href="#"><%- i18n('ssl', 'other') %></a>
23+
<a class="dropdown-item add-item" data-cert="clientca" href="#"><%- i18n('ssl', 'clientca') %></a>
2324
</div>
2425
</div>
2526
<% } %>

frontend/js/i18n/messages.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"ssl": {
100100
"letsencrypt": "Let's Encrypt",
101101
"other": "Custom",
102+
"clientca": "Client Certificate Authority",
102103
"none": "HTTP only",
103104
"letsencrypt-email": "Email Address for Let's Encrypt",
104105
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
@@ -185,14 +186,15 @@
185186
"title": "SSL Certificates",
186187
"empty": "There are no SSL Certificates",
187188
"add": "Add SSL Certificate",
188-
"form-title": "Add {provider, select, letsencrypt{Let's Encrypt} other{Custom}} Certificate",
189+
"form-title": "Add {provider, select, letsencrypt{Let's Encrypt Certificate} other{Custom Certificate} clientca{Client Certificate Authority}}",
189190
"delete": "Delete SSL Certificate",
190191
"delete-confirm": "Are you sure you want to delete this SSL Certificate? Any hosts using it will need to be updated later.",
191192
"help-title": "SSL Certificates",
192193
"help-content": "SSL certificates (correctly known as TLS Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPM uses a service called Let's Encrypt to issue SSL certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPM, it's probably a good idea to use a certificate.\nNPM also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.",
193194
"other-certificate": "Certificate",
194195
"other-certificate-key": "Certificate Key",
195196
"other-intermediate-certificate": "Intermediate Certificate",
197+
"clientca-certificate": "Certificate",
196198
"force-renew": "Renew Now",
197199
"test-reachability": "Test Server Reachability",
198200
"reachability-title": "Test Server Reachability",
@@ -231,7 +233,8 @@
231233
"pass-auth": "Pass Auth to Host",
232234
"access-add": "Add",
233235
"auth-add": "Add",
234-
"search": "Search Access…"
236+
"search": "Search Access…",
237+
"client-certificates": "Client Certificates"
235238
},
236239
"users": {
237240
"title": "Users",

0 commit comments

Comments
 (0)