mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
Merge pull request #136 from alexAubin/certmanager
[enh] Certificate management interface
This commit is contained in:
commit
8f8e5e6ccc
4 changed files with 306 additions and 7 deletions
|
@ -81,12 +81,29 @@
|
||||||
// Get existing domain info
|
// Get existing domain info
|
||||||
app.get('#/domains/:domain', function (c) {
|
app.get('#/domains/:domain', function (c) {
|
||||||
c.api('/domains/main', function(dataMain) {
|
c.api('/domains/main', function(dataMain) {
|
||||||
|
c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8
|
||||||
|
|
||||||
|
// FIXME - This dirty trick (along with the previous API call
|
||||||
|
// for apps installed) should be removed once letsencrypt_ynh
|
||||||
|
// is not used by many people anymore. Probably around 07/2017
|
||||||
|
// or end of 2017...
|
||||||
|
enable_cert_management_ = true;
|
||||||
|
$.each(data['apps'], function(k, v) {
|
||||||
|
if (v.id == "letsencrypt")
|
||||||
|
{
|
||||||
|
enable_cert_management_ = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
domain = {
|
domain = {
|
||||||
name: c.params['domain'],
|
name: c.params['domain'],
|
||||||
main: (c.params['domain'] == dataMain.current_main_domain) ? true : false,
|
main: (c.params['domain'] == dataMain.current_main_domain) ? true : false,
|
||||||
url: "https://"+c.params['domain']
|
url: "https://"+c.params['domain'],
|
||||||
}
|
enable_cert_management: enable_cert_management_
|
||||||
|
};
|
||||||
c.view('domain/domain_info', domain);
|
c.view('domain/domain_info', domain);
|
||||||
|
});
|
||||||
}, 'PUT');
|
}, 'PUT');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,6 +118,164 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Domain certificate
|
||||||
|
app.get('#/domains/:domain/cert-management', function (c) {
|
||||||
|
c.api('/domains/cert-status/' + c.params['domain'] + '?full', function(data) {
|
||||||
|
|
||||||
|
s = data["certificates"][c.params['domain']]
|
||||||
|
|
||||||
|
status_ = {}
|
||||||
|
status_.CA_type = s.CA_type.verbose
|
||||||
|
status_.CA_name = s.CA_name
|
||||||
|
status_.validity = s.validity
|
||||||
|
status_.ACME_eligible = s.ACME_eligible
|
||||||
|
|
||||||
|
switch (s.summary.code)
|
||||||
|
{
|
||||||
|
case "critical" :
|
||||||
|
status_.alert_type = "danger";
|
||||||
|
status_.alert_icon = "exclamation-circle" ;
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_not_valid');
|
||||||
|
break;
|
||||||
|
case "warning" :
|
||||||
|
status_.alert_type = "warning";
|
||||||
|
status_.alert_icon = "exclamation-triangle";
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_selfsigned');
|
||||||
|
break;
|
||||||
|
case "attention" :
|
||||||
|
if (status_.CA_type == "lets-encrypt")
|
||||||
|
{
|
||||||
|
status_.alert_type = "warning";
|
||||||
|
status_.alert_icon = "clock-o";
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_letsencrypt_about_to_expire');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status_.alert_type = "danger";
|
||||||
|
status_.alert_icon = "clock-o";
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_about_to_expire');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "good" :
|
||||||
|
status_.alert_type = "success";
|
||||||
|
status_.alert_icon = "check-circle";
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_good');
|
||||||
|
break;
|
||||||
|
case "great" :
|
||||||
|
status_.alert_type = "success";
|
||||||
|
status_.alert_icon = "thumbs-up";
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_great');
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
status_.alert_type = "warning"
|
||||||
|
status_.alert_icon = "question"
|
||||||
|
status_.alert_message = y18n.t('certificate_alert_unknown');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions_enabled = {};
|
||||||
|
actions_enabled.install_letsencrypt = false;
|
||||||
|
actions_enabled.manual_renew_letsencrpt = false;
|
||||||
|
actions_enabled.regen_selfsigned = false;
|
||||||
|
actions_enabled.replace_with_selfsigned = false;
|
||||||
|
|
||||||
|
switch (s.CA_type.code)
|
||||||
|
{
|
||||||
|
case "self-signed" :
|
||||||
|
actions_enabled.install_letsencrypt = true;
|
||||||
|
actions_enabled.regen_selfsigned = true;
|
||||||
|
break;
|
||||||
|
case "lets-encrypt" :
|
||||||
|
actions_enabled.manual_renew_letsencrpt = true;
|
||||||
|
actions_enabled.replace_with_selfsigned = true;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
actions_enabled.replace_with_selfsigned = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_ = {
|
||||||
|
name: c.params['domain'],
|
||||||
|
status: status_,
|
||||||
|
actions_enabled : actions_enabled
|
||||||
|
};
|
||||||
|
c.view('domain/domain_cert', data_);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Install let's encrypt certificate on domain
|
||||||
|
app.get('#/domains/:domain/cert-install-LE', function (c) {
|
||||||
|
c.confirm(
|
||||||
|
y18n.t('certificate'),
|
||||||
|
y18n.t('confirm_cert_install_LE', [c.params['domain']]),
|
||||||
|
function(){
|
||||||
|
c.api('/domains/cert-install/' + c.params['domain'], function(data) {
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}, 'POST');
|
||||||
|
},
|
||||||
|
function(){
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regenerate a self-signed certificate
|
||||||
|
app.get('#/domains/:domain/cert-regen-selfsigned', function (c) {
|
||||||
|
c.confirm(
|
||||||
|
y18n.t('certificate'),
|
||||||
|
y18n.t('confirm_cert_regen_selfsigned', [c.params['domain']]),
|
||||||
|
function(){
|
||||||
|
c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed", function(data) {
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}, 'POST');
|
||||||
|
},
|
||||||
|
function(){
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manually renew a Let's Encrypt certificate
|
||||||
|
app.get('#/domains/:domain/cert-renew-letsencrypt', function (c) {
|
||||||
|
c.confirm(
|
||||||
|
y18n.t('certificate'),
|
||||||
|
y18n.t('confirm_cert_manual_renew_LE', [c.params['domain']]),
|
||||||
|
function(){
|
||||||
|
c.api('/domains/cert-renew/' + c.params['domain'] + "?force", function(data) {
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}, 'POST');
|
||||||
|
},
|
||||||
|
function(){
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace valid cert with self-signed
|
||||||
|
app.get('#/domains/:domain/cert-replace-with-selfsigned', function (c) {
|
||||||
|
c.confirm(
|
||||||
|
y18n.t('certificate'),
|
||||||
|
y18n.t('confirm_cert_revert_to_selfsigned', [c.params['domain']]),
|
||||||
|
function(){
|
||||||
|
c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed&force", function(data) {
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}, 'POST');
|
||||||
|
},
|
||||||
|
function(){
|
||||||
|
store.clear('slide');
|
||||||
|
c.redirect('#/domains/'+c.params['domain']+'/cert-management');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Remove existing domain
|
// Remove existing domain
|
||||||
app.get('#/domains/:domain/delete', function (c) {
|
app.get('#/domains/:domain/delete', function (c) {
|
||||||
c.confirm(
|
c.confirm(
|
||||||
|
|
|
@ -294,5 +294,33 @@
|
||||||
"warning_first_user": "You probably need to <a href='#/users/create' class='alert-link'>create a user</a> first.",
|
"warning_first_user": "You probably need to <a href='#/users/create' class='alert-link'>create a user</a> first.",
|
||||||
"write": "Write",
|
"write": "Write",
|
||||||
"wrong_password": "Wrong password",
|
"wrong_password": "Wrong password",
|
||||||
"yes": "Yes"
|
"yes": "Yes",
|
||||||
|
"certificate_alert_not_valid": "CRITICAL : Certificate is not valid ! HTTPS won't work at all !",
|
||||||
|
"certificate_alert_selfsigned": "WARNING : Current certificate is self-signed. Browsers will display a spooky warning to new visitors !",
|
||||||
|
"certificate_alert_letsencrypt_about_to_expire": "Attention : Certificate is about to expire. It should soon be renewed automatically.",
|
||||||
|
"certificate_alert_about_to_expire": "WARNING : Certificate is about to expire ! It will NOT be renewed automatically !",
|
||||||
|
"certificate_alert_good": "Okay, certificate looks good !",
|
||||||
|
"certificate_alert_great": "Great ! You're using a valid Let's Encrypt certificate !",
|
||||||
|
"certificate_alert_unknown": "Unknown status",
|
||||||
|
"certificate_manage" : "Manage SSL certificate",
|
||||||
|
"certificate_old_letsencrypt_app_conflict" : "The 'letsencrypt' app is currently installed and conflicts with this feature. Please uninstall it first to use the new certificate management interface.",
|
||||||
|
"ssl_certificate" : "SSL certificate",
|
||||||
|
"confirm_cert_install_LE": "Are you sure you want to install a Let's Encrypt certificate for this domain.",
|
||||||
|
"confirm_cert_regen_selfsigned": "Are you sure you want to regenerate a self-signed certificate for this domain ?",
|
||||||
|
"confirm_cert_manual_renew_LE": "Are you sure you want to manually renew the Let's Encrypt certificate for this domain now ?",
|
||||||
|
"confirm_cert_revert_to_selfsigned": "Are you sure you want to revert this domain to a self-signed certificate ?",
|
||||||
|
"certificate" : "Certificate",
|
||||||
|
"certificate_status" : "Certificate status",
|
||||||
|
"certificate_authority" : "Certification authority",
|
||||||
|
"validity" : "Validity",
|
||||||
|
"domain_is_eligible_for_ACME" : "This domain seems correctly configured to install a Let's Encrypt certificate !",
|
||||||
|
"domain_not_eligible_for_ACME" : "This domain doesn't seem ready for a Let's Encrypt certificate. Please check your DNS configuration and HTTP server reachability.",
|
||||||
|
"install_letsencrypt_cert" : "Install a Let's Encrypt certificate",
|
||||||
|
"manually_renew_letsencrypt_message" : "Certificate will be automatically renewed during the last 15 days of validity. You can manually renew it if you want to. (Not recommended).",
|
||||||
|
"manually_renew_letsencrypt" : "Manually renew now",
|
||||||
|
"regenerate_selfsigned_cert_message" : "If you want, you can regenerate the self-signed certificate.",
|
||||||
|
"regenerate_selfsigned_cert" : "Regenerate self-signed certificate",
|
||||||
|
"revert_to_selfsigned_cert_message" : "If you really want to, you can reinstall a self-signed certificate. (Not recommended)",
|
||||||
|
"revert_to_selfsigned_cert" : "Revert to a self-signed certificate"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
src/views/domain/domain_cert.ms
Normal file
84
src/views/domain/domain_cert.ms
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<div class="btn-breadcrumb">
|
||||||
|
<a href="#/" ><i class="fa-home"></i><span class="sr-only">{{t 'home'}}</span></a>
|
||||||
|
<a href="#/domains">{{t 'domains'}}</a>
|
||||||
|
<a href="#/domains/{{name}}">{{name}}</a>
|
||||||
|
<a href="#/domains/{{name}}/cert-management">{{t 'certificate'}}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h2 class="panel-title">
|
||||||
|
<span class="fa-fw fa-lock"></span> {{t 'certificate_status'}}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
<div class="alert alert-{{status.alert_type}}">
|
||||||
|
<span class="fa-fw fa-{{status.alert_icon}}"></span> {{status.alert_message}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>{{t 'certificate_authority'}}</dt>
|
||||||
|
<dd>{{status.CA_type}} ({{status.CA_name}})</dd>
|
||||||
|
<dt>{{t 'validity'}}</dt>
|
||||||
|
<dd>{{status.validity}} days</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h2 class="panel-title">
|
||||||
|
<span class="fa-fw fa-wrench"></span> {{t 'operations'}}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
{{#if actions_enabled.install_letsencrypt}}
|
||||||
|
<div class="container">
|
||||||
|
{{#if status.ACME_eligible}}
|
||||||
|
<p><span class="fa-fw fa-check"></span>
|
||||||
|
{{t 'domain_is_eligible_for_ACME'}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p><span class="fa-fw fa-meh-o"></span>
|
||||||
|
{{t 'domain_not_eligible_for_ACME'}}</p>
|
||||||
|
{{/if}}
|
||||||
|
<a href="#/domains/{{name}}/cert-install-LE" class="btn btn-success {{#unless status.ACME_eligible}}disabled{{/unless}}">
|
||||||
|
<span class="fa-star"></span> {{t 'install_letsencrypt_cert'}}
|
||||||
|
</a>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if actions_enabled.manual_renew_letsencrpt}}
|
||||||
|
<div class="container">
|
||||||
|
<p>{{t 'manually_renew_letsencrypt_message'}}</p>
|
||||||
|
<a href="#/domains/{{name}}/cert-renew-letsencrypt" class="btn btn-warning">
|
||||||
|
<span class="fa-refresh"></span> {{t 'manually_renew_letsencrypt'}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{{/if}}
|
||||||
|
{{#if actions_enabled.regen_selfsigned}}
|
||||||
|
<div class="container">
|
||||||
|
<p>{{t 'regenerate_selfsigned_cert_message'}}</p>
|
||||||
|
<a href="#/domains/{{name}}/cert-regen-selfsigned" class="btn btn-warning">
|
||||||
|
<span class="fa-refresh"></span> {{t 'regenerate_selfsigned_cert'}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{{/if}}
|
||||||
|
{{#if actions_enabled.replace_with_selfsigned}}
|
||||||
|
<div class="container">
|
||||||
|
<p>{{t 'revert_to_selfsigned_cert_message'}}</p>
|
||||||
|
<a href="#/domains/{{name}}/cert-replace-with-selfsigned" class="btn btn-danger">
|
||||||
|
<span class="fa-exclamation-triangle"></span> {{t 'revert_to_selfsigned_cert'}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,18 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
<div class="container">
|
||||||
|
<p>{{t 'certificate_manage'}}</p>
|
||||||
|
{{#unless enable_cert_management}}
|
||||||
|
<p><span class="fa-fw fa-exclamation-circle"></span>
|
||||||
|
{{t 'certificate_old_letsencrypt_app_conflict'}}
|
||||||
|
</p>
|
||||||
|
{{/unless}}
|
||||||
|
<a href="#/domains/{{name}}/cert-management" class="btn btn-default slide {{#unless enable_cert_management}}disabled{{/unless}}">
|
||||||
|
{{t 'ssl_certificate'}} <span class="fa-fw fa-lock"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p>{{t 'domain_delete_longdesc' name}}</p>
|
<p>{{t 'domain_delete_longdesc' name}}</p>
|
||||||
<a href="#/domains/{{name}}/delete" class="btn btn-danger slide back">
|
<a href="#/domains/{{name}}/delete" class="btn btn-danger slide back">
|
||||||
|
|
Loading…
Reference in a new issue