Merge pull request #136 from alexAubin/certmanager

[enh] Certificate management interface
This commit is contained in:
Zamentur aka ljf 2016-12-07 00:48:05 +01:00 committed by GitHub
commit 8f8e5e6ccc
4 changed files with 306 additions and 7 deletions

View file

@ -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(

View file

@ -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"
} }

View 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>

View file

@ -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">