diff --git a/docs/administration.rst b/docs/administration.rst index 846a4c34..2f71c0bf 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -172,15 +172,16 @@ Specifying the `SQLALCHEMY_MAX_OVERFLOW` to 0 will enforce limit to not create c PUBLIC_CA_MAX_VALIDITY_DAYS = 365 -.. data:: DEFAULT_MAX_VALIDITY_DAYS +.. data:: DEFAULT_VALIDITY_DAYS :noindex: - Use this config to override the default limit of 1095 days (3 years) of validity. Any CA which is not listed in - PUBLIC_CA_AUTHORITY_NAMES will be using this validity to display date range on UI. Below example overrides the - default validity of 1095 days and sets it to 365 days. + Use this config to override the default validity of 365 days for certificates offered through Lemur UI. Any CA which + is not listed in PUBLIC_CA_AUTHORITY_NAMES will be using this value as default validity to be displayed on UI. Please + note that this config is used for cert issuance only through Lemur UI. Below example overrides the default validity + of 365 days and sets it to 1095 days (3 years). :: - DEFAULT_MAX_VALIDITY_DAYS = 365 + DEFAULT_VALIDITY_DAYS = 1095 .. data:: DEBUG_DUMP diff --git a/lemur/authorities/schemas.py b/lemur/authorities/schemas.py index 7f9f57d4..ef6263a8 100644 --- a/lemur/authorities/schemas.py +++ b/lemur/authorities/schemas.py @@ -112,6 +112,7 @@ class RootAuthorityCertificateOutputSchema(LemurOutputSchema): not_after = fields.DateTime() not_before = fields.DateTime() max_issuance_days = fields.Integer() + default_validity_days = fields.Integer() owner = fields.Email() status = fields.Boolean() user = fields.Nested(UserNestedOutputSchema) @@ -137,7 +138,7 @@ class AuthorityNestedOutputSchema(LemurOutputSchema): owner = fields.Email() plugin = fields.Nested(PluginOutputSchema) active = fields.Boolean() - authority_certificate = fields.Nested(RootAuthorityCertificateOutputSchema, only=["max_issuance_days"]) + authority_certificate = fields.Nested(RootAuthorityCertificateOutputSchema, only=["max_issuance_days", "default_validity_days"]) authority_update_schema = AuthorityUpdateSchema() diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index a52ec1a8..675cecb4 100644 --- a/lemur/certificates/models.py +++ b/lemur/certificates/models.py @@ -320,7 +320,13 @@ class Certificate(db.Model): if self.name.lower() in [ca.lower() for ca in public_CA]: return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397) - return current_app.config.get("DEFAULT_MAX_VALIDITY_DAYS", 1095) # 3 years default + @property + def default_validity_days(self): + public_CA = current_app.config.get("PUBLIC_CA_AUTHORITY_NAMES", []) + if self.name.lower() in [ca.lower() for ca in public_CA]: + return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397) + + return current_app.config.get("DEFAULT_VALIDITY_DAYS", 365) # 1 year default @property def subject(self): diff --git a/lemur/plugins/lemur_digicert/plugin.py b/lemur/plugins/lemur_digicert/plugin.py index fd8c4e2d..3948acbb 100644 --- a/lemur/plugins/lemur_digicert/plugin.py +++ b/lemur/plugins/lemur_digicert/plugin.py @@ -18,8 +18,9 @@ import json import arrow import pem import requests +import sys from cryptography import x509 -from flask import current_app +from flask import current_app, g from lemur.common.utils import validate_conf from lemur.extensions import metrics from lemur.plugins import lemur_digicert as digicert @@ -129,6 +130,9 @@ def map_fields(options, csr): data["validity_years"] = determine_validity_years(options.get("validity_years")) elif options.get("validity_end"): data["custom_expiration_date"] = determine_end_date(options.get("validity_end")).format("YYYY-MM-DD") + # check if validity got truncated. If resultant validity is not equal to requested validity, it just got truncated + if data["custom_expiration_date"] != options.get("validity_end").format("YYYY-MM-DD"): + log_validity_truncation(options, f"{__name__}.{sys._getframe().f_code.co_name}") else: data["validity_years"] = determine_validity_years(0) @@ -154,6 +158,9 @@ def map_cis_fields(options, csr): validity_end = determine_end_date(arrow.utcnow().shift(years=options["validity_years"])) elif options.get("validity_end"): validity_end = determine_end_date(options.get("validity_end")) + # check if validity got truncated. If resultant validity is not equal to requested validity, it just got truncated + if validity_end != options.get("validity_end"): + log_validity_truncation(options, f"{__name__}.{sys._getframe().f_code.co_name}") else: validity_end = determine_end_date(False) @@ -179,6 +186,18 @@ def map_cis_fields(options, csr): return data +def log_validity_truncation(options, function): + log_data = { + "cn": options["common_name"], + "creator": g.user.username + } + metrics.send("digicert_validity_truncated", "counter", 1, metric_tags=log_data) + + log_data["function"] = function + log_data["message"] = "Digicert Plugin truncated the validity of certificate" + current_app.logger.info(log_data) + + def handle_response(response): """ Handle the DigiCert API response and any errors it might have experienced. diff --git a/lemur/static/app/angular/certificates/certificate/certificate.js b/lemur/static/app/angular/certificates/certificate/certificate.js index 155658e6..6b275328 100644 --- a/lemur/static/app/angular/certificates/certificate/certificate.js +++ b/lemur/static/app/angular/certificates/certificate/certificate.js @@ -107,7 +107,6 @@ angular.module('lemur') startingDay: 1 }; - $scope.open1 = function() { $scope.popup1.opened = true; }; @@ -140,6 +139,14 @@ angular.module('lemur') ); $scope.create = function (certificate) { + if(certificate.validityType === 'customDates' && + (!certificate.validityStart || !certificate.validityEnd)) { // these are not mandatory fields in schema, thus handling validation in js + return showMissingDateError(); + } + if(certificate.validityType === 'defaultDays') { + populateValidityDateAsPerDefault(certificate); + } + WizardHandler.wizard().context.loading = true; CertificateService.create(certificate).then( function () { @@ -164,6 +171,30 @@ angular.module('lemur') }); }; + function showMissingDateError() { + let error = {}; + error.message = ''; + error.reasons = {}; + error.reasons.validityRange = 'Valid start and end dates are needed, else select Default option'; + + toaster.pop({ + type: 'error', + title: 'Validation Error', + body: 'lemur-bad-request', + bodyOutputType: 'directive', + directiveData: error, + timeout: 100000 + }); + } + + function populateValidityDateAsPerDefault(certificate) { + // calculate start and end date as per default validity + let startDate = new Date(), endDate = new Date(); + endDate.setDate(startDate.getDate() + certificate.authority.authorityCertificate.defaultValidityDays); + certificate.validityStart = startDate; + certificate.validityEnd = endDate; + } + $scope.templates = [ { 'name': 'Client Certificate', @@ -277,6 +308,14 @@ angular.module('lemur') }; $scope.create = function (certificate) { + if(certificate.validityType === 'customDates' && + (!certificate.validityStart || !certificate.validityEnd)) { // these are not mandatory fields in schema, thus handling validation in js + return showMissingDateError(); + } + if(certificate.validityType === 'defaultDays') { + populateValidityDateAsPerDefault(certificate); + } + WizardHandler.wizard().context.loading = true; CertificateService.create(certificate).then( function () { @@ -301,6 +340,30 @@ angular.module('lemur') }); }; + function showMissingDateError() { + let error = {}; + error.message = ''; + error.reasons = {}; + error.reasons.validityRange = 'Valid start and end dates are needed, else select Default option'; + + toaster.pop({ + type: 'error', + title: 'Validation Error', + body: 'lemur-bad-request', + bodyOutputType: 'directive', + directiveData: error, + timeout: 100000 + }); + } + + function populateValidityDateAsPerDefault(certificate) { + // calculate start and end date as per default validity + let startDate = new Date(), endDate = new Date(); + endDate.setDate(startDate.getDate() + certificate.authority.authorityCertificate.defaultValidityDays); + certificate.validityStart = startDate; + certificate.validityEnd = endDate; + } + $scope.templates = [ { 'name': 'Client Certificate', diff --git a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html index 07d6b0f4..d60a1a6a 100644 --- a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html @@ -96,7 +96,7 @@ Certificate Authority
- + {{$select.selected.name}}
-
- +
+
+ + +
- - or - -
+
-
+
-
- -