Merge branch 'master' into master

This commit is contained in:
Hossein Shafagh 2020-09-15 09:14:15 -07:00 committed by GitHub
commit 305de2217e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 471 additions and 50 deletions

View File

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

View File

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

View File

@ -9,9 +9,10 @@ from datetime import timedelta
import arrow
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import rsa, ec
from flask import current_app
from idna.core import InvalidCodepoint
from lemur.common.utils import get_key_type_from_ec_curve
from sqlalchemy import (
event,
Integer,
@ -302,6 +303,8 @@ class Certificate(db.Model):
return "RSA{key_size}".format(
key_size=self.parsed_cert.public_key().key_size
)
elif isinstance(self.parsed_cert.public_key(), ec.EllipticCurvePublicKey):
return get_key_type_from_ec_curve(self.parsed_cert.public_key().curve.name)
@property
def validity_remaining(self):
@ -317,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):

View File

@ -148,6 +148,13 @@ class CertificateInputSchema(CertificateCreationSchema):
data["extensions"]["subAltNames"]["names"] = []
data["extensions"]["subAltNames"]["names"] = csr_sans
common_name = cert_utils.get_cn_from_csr(data["csr"])
if common_name:
data["common_name"] = common_name
key_type = cert_utils.get_key_type_from_csr(data["csr"])
if key_type:
data["key_type"] = key_type
return missing.convert_validity_years(data)

View File

@ -12,6 +12,8 @@ Utils to parse certificate data.
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from marshmallow.exceptions import ValidationError
from cryptography.hazmat.primitives.asymmetric import rsa, ec
from lemur.common.utils import get_key_type_from_ec_curve
def get_sans_from_csr(data):
@ -39,3 +41,45 @@ def get_sans_from_csr(data):
pass
return sub_alt_names
def get_cn_from_csr(data):
"""
Fetches common name (CN) from CSR.
Works with any kind of SubjectAlternativeName
:param data: PEM-encoded string with CSR
:return: the common name
"""
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
common_name = request.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
return common_name[0].value
def get_key_type_from_csr(data):
"""
Fetches key_type from CSR.
Works with any kind of SubjectAlternativeName
:param data: PEM-encoded string with CSR
:return: key_type
"""
try:
request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
except Exception:
raise ValidationError("CSR presented is not valid.")
try:
if isinstance(request.public_key(), rsa.RSAPublicKey):
return "RSA{key_size}".format(
key_size=request.public_key().key_size
)
elif isinstance(request.public_key(), ec.EllipticCurvePublicKey):
return get_key_type_from_ec_curve(request.public_key().curve.name)
else:
raise Exception("Unsupported key type")
except NotImplemented:
raise NotImplemented()

View File

@ -114,6 +114,39 @@ def get_authority_key(body):
return authority_key.hex()
def get_key_type_from_ec_curve(curve_name):
"""
Give an EC curve name, return the matching key_type.
:param: curve_name
:return: key_type
"""
_CURVE_TYPES = {
ec.SECP192R1().name: "ECCPRIME192V1",
ec.SECP256R1().name: "ECCPRIME256V1",
ec.SECP224R1().name: "ECCSECP224R1",
ec.SECP384R1().name: "ECCSECP384R1",
ec.SECP521R1().name: "ECCSECP521R1",
ec.SECP256K1().name: "ECCSECP256K1",
ec.SECT163K1().name: "ECCSECT163K1",
ec.SECT233K1().name: "ECCSECT233K1",
ec.SECT283K1().name: "ECCSECT283K1",
ec.SECT409K1().name: "ECCSECT409K1",
ec.SECT571K1().name: "ECCSECT571K1",
ec.SECT163R2().name: "ECCSECT163R2",
ec.SECT233R1().name: "ECCSECT233R1",
ec.SECT283R1().name: "ECCSECT283R1",
ec.SECT409R1().name: "ECCSECT409R1",
ec.SECT571R1().name: "ECCSECT571R2",
}
if curve_name in _CURVE_TYPES.keys():
return _CURVE_TYPES[curve_name]
else:
return None
def generate_private_key(key_type):
"""
Generates a new private key based on key_type.
@ -128,11 +161,11 @@ def generate_private_key(key_type):
"""
_CURVE_TYPES = {
"ECCPRIME192V1": ec.SECP192R1(),
"ECCPRIME256V1": ec.SECP256R1(),
"ECCSECP192R1": ec.SECP192R1(),
"ECCPRIME192V1": ec.SECP192R1(), # duplicate
"ECCPRIME256V1": ec.SECP256R1(), # duplicate
"ECCSECP192R1": ec.SECP192R1(), # duplicate
"ECCSECP224R1": ec.SECP224R1(),
"ECCSECP256R1": ec.SECP256R1(),
"ECCSECP256R1": ec.SECP256R1(), # duplicate
"ECCSECP384R1": ec.SECP384R1(),
"ECCSECP521R1": ec.SECP521R1(),
"ECCSECP256K1": ec.SECP256K1(),

View File

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

View File

@ -0,0 +1,5 @@
"""Set the version information."""
try:
VERSION = __import__("pkg_resources").get_distribution(__name__).version
except Exception as e:
VERSION = "unknown"

View File

@ -0,0 +1,228 @@
from lemur.plugins.bases import IssuerPlugin, SourcePlugin
import arrow
import requests
import json
from lemur.plugins import lemur_entrust as ENTRUST
from flask import current_app
from lemur.extensions import metrics
from lemur.common.utils import validate_conf
def log_status_code(r, *args, **kwargs):
"""
Is a request hook that logs all status codes to the ENTRUST api.
:param r:
:param args:
:param kwargs:
:return:
"""
metrics.send("ENTRUST_status_code_{}".format(r.status_code), "counter", 1)
def determine_end_date(end_date):
"""
Determine appropriate end date
:param end_date:
:return: validity_end
"""
# ENTRUST only allows 13 months of max certificate duration
max_validity_end = arrow.utcnow().shift(years=1, months=+1).format('YYYY-MM-DD')
if not end_date:
end_date = max_validity_end
if end_date > max_validity_end:
end_date = max_validity_end
return end_date
def process_options(options):
"""
Processes and maps the incoming issuer options to fields/options that
Entrust understands
:param options:
:return: dict of valid entrust options
"""
# if there is a config variable ENTRUST_PRODUCT_<upper(authority.name)>
# take the value as Cert product-type
# else default to "STANDARD_SSL"
authority = options.get("authority").name.upper()
product_type = current_app.config.get("ENTRUST_PRODUCT_{0}".format(authority), "STANDARD_SSL")
if options.get("validity_end"):
validity_end = determine_end_date(options.get("validity_end"))
else:
validity_end = determine_end_date(False)
tracking_data = {
"requesterName": current_app.config.get("ENTRUST_NAME"),
"requesterEmail": current_app.config.get("ENTRUST_EMAIL"),
"requesterPhone": current_app.config.get("ENTRUST_PHONE")
}
data = {
"signingAlg": "SHA-2",
"eku": "SERVER_AND_CLIENT_AUTH",
"certType": product_type,
"certExpiryDate": validity_end,
"tracking": tracking_data
}
return data
def handle_response(my_response):
"""
Helper function for parsing responses from the Entrust API.
:param content:
:return: :raise Exception:
"""
msg = {
200: "The request had the validateOnly flag set to true and validation was successful.",
201: "Certificate created",
202: "Request accepted and queued for approval",
400: "Invalid request parameters",
404: "Unknown jobId",
429: "Too many requests"
}
try:
d = json.loads(my_response.content)
except Exception as e:
# catch an empty jason object here
d = {'errors': 'No detailled message'}
s = my_response.status_code
if s > 399:
raise Exception("ENTRUST error: {0}\n{1}".format(msg.get(s, s), d['errors']))
current_app.logger.info("Response: {0}, {1} ".format(s, d))
return d
class EntrustIssuerPlugin(IssuerPlugin):
title = "ENTRUST"
slug = "entrust-issuer"
description = "Enables the creation of certificates by ENTRUST"
version = ENTRUST.VERSION
author = "sirferl"
author_url = "https://github.com/sirferl/lemur"
def __init__(self, *args, **kwargs):
"""Initialize the issuer with the appropriate details."""
required_vars = [
"ENTRUST_API_CERT",
"ENTRUST_API_KEY",
"ENTRUST_API_USER",
"ENTRUST_API_PASS",
"ENTRUST_URL",
"ENTRUST_ROOT",
"ENTRUST_NAME",
"ENTRUST_EMAIL",
"ENTRUST_PHONE",
"ENTRUST_ISSUING",
]
validate_conf(current_app, required_vars)
self.session = requests.Session()
cert_file = current_app.config.get("ENTRUST_API_CERT")
key_file = current_app.config.get("ENTRUST_API_KEY")
user = current_app.config.get("ENTRUST_API_USER")
password = current_app.config.get("ENTRUST_API_PASS")
self.session.cert = (cert_file, key_file)
self.session.auth = (user, password)
self.session.hooks = dict(response=log_status_code)
# self.session.config['keep_alive'] = False
super(EntrustIssuerPlugin, self).__init__(*args, **kwargs)
def create_certificate(self, csr, issuer_options):
"""
Creates an Entrust certificate.
:param csr:
:param issuer_options:
:return: :raise Exception:
"""
current_app.logger.info(
"Requesting options: {0}".format(issuer_options)
)
url = current_app.config.get("ENTRUST_URL") + "/certificates"
data = process_options(issuer_options)
data["csr"] = csr
try:
response = self.session.post(url, json=data, timeout=(15, 40))
except requests.exceptions.Timeout:
raise Exception("Timeout for POST")
except requests.exceptions.RequestException as e:
raise Exception("Error for POST {0}".format(e))
response_dict = handle_response(response)
external_id = response_dict['trackingId']
cert = response_dict['endEntityCert']
chain = response_dict['chainCerts'][1]
current_app.logger.info(
"Received Chain: {0}".format(chain)
)
return cert, chain, external_id
def revoke_certificate(self, certificate, comments):
"""Revoke a Digicert certificate."""
base_url = current_app.config.get("ENTRUST_URL")
# make certificate revoke request
revoke_url = "{0}/certificates/{1}/revocations".format(
base_url, certificate.external_id
)
metrics.send("entrust_revoke_certificate", "counter", 1)
if comments == '' or not comments:
comments = "revoked via API"
data = {
"crlReason": "superseded",
"revocationComment": comments
}
response = self.session.post(revoke_url, json=data)
data = handle_response(response)
@staticmethod
def create_authority(options):
"""Create an authority.
Creates an authority, this authority is then used by Lemur to
allow a user to specify which Certificate Authority they want
to sign their certificate.
:param options:
:return:
"""
entrust_root = current_app.config.get("ENTRUST_ROOT")
entrust_issuing = current_app.config.get("ENTRUST_ISSUING")
role = {"username": "", "password": "", "name": "entrust"}
current_app.logger.info("Creating Auth: {0} {1}".format(options, entrust_issuing))
return entrust_root, "", [role]
def get_ordered_certificate(self, order_id):
raise NotImplementedError("Not implemented\n", self, order_id)
def canceled_ordered_certificate(self, pending_cert, **kwargs):
raise NotImplementedError("Not implemented\n", self, pending_cert, **kwargs)
class EntrustSourcePlugin(SourcePlugin):
title = "ENTRUST"
slug = "entrust-source"
description = "Enables the collecion of certificates"
version = ENTRUST.VERSION
author = "sirferl"
author_url = "https://github.com/sirferl/lemur"
def get_certificates(self, options, **kwargs):
# Not needed for ENTRUST
raise NotImplementedError("Not implemented\n", self, options, **kwargs)
def get_endpoints(self, options, **kwargs):
# There are no endpoints in ENTRUST
raise NotImplementedError("Not implemented\n", self, options, **kwargs)

View File

@ -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',

View File

@ -20,7 +20,7 @@
name="certificate signing request"
ng-model="certificate.csr"
placeholder="PEM encoded string..." class="form-control"
ng-pattern="/^-----BEGIN CERTIFICATE REQUEST-----/"></textarea>
ng-pattern="/(^-----BEGIN CERTIFICATE REQUEST-----[\S\s]*-----END CERTIFICATE REQUEST-----)|(^-----BEGIN NEW CERTIFICATE REQUEST-----[\S\s]*-----END NEW CERTIFICATE REQUEST-----)/"></textarea>
<p ng-show="trackingForm.csr.$invalid && !trackingForm.csr.$pristine"
class="help-block">Enter a valid certificate signing request.</p>

View File

@ -96,7 +96,7 @@
Certificate Authority
</label>
<div class="col-sm-10">
<ui-select class="input-md" ng-model="certificate.authority" theme="bootstrap" title="choose an authority">
<ui-select class="input-md" ng-model="certificate.authority" theme="bootstrap" title="choose an authority" ng-change="clearDates()">
<ui-select-match placeholder="select an authority...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices class="form-control" repeat="authority in authorities"
refresh="getAuthoritiesByName($select.search)"
@ -133,22 +133,20 @@
</div>
<div class="form-group" ng-hide="certificate.authority.plugin.slug == 'acme-issuer'">
<label class="control-label col-sm-2"
uib-tooltip="If no date is selected Lemur attempts to issue a 1 year certificate">
uib-tooltip="You can select custom date range; however, we recommend continuing with default validity.">
Validity Range <span class="glyphicon glyphicon-question-sign"></span>
</label>
<div class="col-sm-2">
<select ng-model="certificate.validityYears" class="form-control">
<option value="">-</option>
<option value="1">1 year</option>
</select>
<div class="col-sm-4">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-info" ng-model="certificate.validityType" uib-btn-radio="'defaultDays'" ng-click="clearDates()">
Default ({{certificate.authority.authorityCertificate.defaultValidityDays}} days)</label>
<label class="btn btn-info" ng-model="certificate.validityType" uib-btn-radio="'customDates'" ng-change="clearDates()">Custom</label>
</div>
</div>
<span style="padding-top: 15px" class="text-center col-sm-1">
<strong>or</strong>
</span>
<div class="col-sm-3">
<div class="col-sm-3" ng-if="certificate.validityType==='customDates'">
<div class="input-group">
<input type="text" class="form-control"
uib-tooltip="yyyy/MM/dd"
uib-tooltip="Start Date (yyyy/MM/dd)"
uib-datepicker-popup="yyyy/MM/dd"
ng-model="certificate.validityStart"
ng-change="certificate.setValidityEndDateRange(certificate.validityStart)"
@ -159,6 +157,7 @@
min-date="certificate.authority.authorityCertificate.notBefore"
alt-input-formats="altInputFormats"
placeholder="Start Date"
readonly="true"
/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i
@ -166,10 +165,10 @@
</span>
</div>
</div>
<div class="col-sm-3">
<div class="col-sm-3" ng-if="certificate.validityType==='customDates'">
<div class="input-group">
<input type="text" class="form-control"
uib-tooltip="yyyy/MM/dd"
uib-tooltip="End Date (yyyy/MM/dd)"
uib-datepicker-popup="yyyy/MM/dd"
ng-model="certificate.validityEnd"
is-open="popup2.opened"
@ -179,6 +178,7 @@
min-date="certificate.authority.authorityCertificate.minValidityEnd"
alt-input-formats="altInputFormats"
placeholder="End Date"
readonly="true"
/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open2()"><i
@ -186,10 +186,6 @@
</span>
</div>
</div>
<div class="col-sm-1">
<button uib-tooltip="Clear Validity" ng-click="clearDates()" class="btn btn-default"><i
class="glyphicon glyphicon-remove"></i></button>
</div>
</div>
<div class="form-group" ng-show="certificate.authority.plugin.slug == 'acme-issuer'">
<label class="control-label col-sm-2">

View File

@ -167,17 +167,19 @@ angular.module('lemur')
},
setValidityEndDateRange: function (value) {
// clear selected validity end date as we are about to calculate new range
if(this.validityEnd) {
this.validityEnd = '';
}
this.validityEnd = '';
// Minimum end date will be same as selected start date
this.authority.authorityCertificate.minValidityEnd = value;
// Move max end date by maxIssuanceDays
let endDate = new Date(value);
endDate.setDate(endDate.getDate() + this.authority.authorityCertificate.maxIssuanceDays);
this.authority.authorityCertificate.maxValidityEnd = endDate;
if(!this.authority.authorityCertificate || !this.authority.authorityCertificate.maxIssuanceDays) {
this.authority.authorityCertificate.maxValidityEnd = this.authority.authorityCertificate.notAfter;
} else {
// Move max end date by maxIssuanceDays
let endDate = new Date(value);
endDate.setDate(endDate.getDate() + this.authority.authorityCertificate.maxIssuanceDays);
this.authority.authorityCertificate.maxValidityEnd = endDate;
}
}
});
});
@ -195,7 +197,7 @@ angular.module('lemur')
CertificateService.create = function (certificate) {
certificate.attachSubAltName();
certificate.attachCustom();
if (certificate.validityYears === '') { // if a user de-selects validity years we ignore it
if (certificate.validityYears === '') { // if a user de-selects validity years we ignore it - might not be needed anymore
delete certificate.validityYears;
}
return CertificateApi.post(certificate);
@ -281,6 +283,9 @@ angular.module('lemur')
certificate.authority.authorityCertificate.minValidityEnd = defaults.authority.authorityCertificate.notBefore;
certificate.authority.authorityCertificate.maxValidityEnd = defaults.authority.authorityCertificate.notAfter;
// pre-select validity type radio button to default days
certificate.validityType = 'defaultDays';
if (certificate.dnsProviderId) {
certificate.dnsProvider = {id: certificate.dnsProviderId};
}

View File

@ -147,17 +147,19 @@ angular.module('lemur')
},
setValidityEndDateRange: function (value) {
// clear selected validity end date as we are about to calculate new range
if(this.validityEnd) {
this.validityEnd = '';
}
this.validityEnd = '';
// Minimum end date will be same as selected start date
this.authority.authorityCertificate.minValidityEnd = value;
// Move max end date by maxIssuanceDays
let endDate = new Date(value);
endDate.setDate(endDate.getDate() + this.authority.authorityCertificate.maxIssuanceDays);
this.authority.authorityCertificate.maxValidityEnd = endDate;
if(!this.authority.authorityCertificate || !this.authority.authorityCertificate.maxIssuanceDays) {
this.authority.authorityCertificate.maxValidityEnd = this.authority.authorityCertificate.notAfter;
} else {
// Move max end date by maxIssuanceDays
let endDate = new Date(value);
endDate.setDate(endDate.getDate() + this.authority.authorityCertificate.maxIssuanceDays);
this.authority.authorityCertificate.maxValidityEnd = endDate;
}
}
});
});

View File

@ -11,6 +11,12 @@ from lemur.tests.vectors import (
)
def test_get_key_type_from_ec_curve():
from lemur.common.utils import get_key_type_from_ec_curve
assert get_key_type_from_ec_curve("secp256r1") == "ECCPRIME256V1"
def test_generate_private_key():
from lemur.common.utils import generate_private_key

View File

@ -153,7 +153,9 @@ setup(
'vault_source = lemur.plugins.lemur_vault_dest.plugin:VaultSourcePlugin',
'vault_desination = lemur.plugins.lemur_vault_dest.plugin:VaultDestinationPlugin',
'adcs_issuer = lemur.plugins.lemur_adcs.plugin:ADCSIssuerPlugin',
'adcs_source = lemur.plugins.lemur_adcs.plugin:ADCSSourcePlugin'
'adcs_source = lemur.plugins.lemur_adcs.plugin:ADCSSourcePlugin',
'entrust_issuer = lemur.plugins.lemur_entrust.plugin:EntrustIssuerPlugin',
'entrust_source = lemur.plugins.lemur_entrust.plugin:EntrustSourcePlugin'
],
},
classifiers=[