Merge branch 'entrust-plugin' of github.com:sirferl/lemur into entrust-plugin

This commit is contained in:
sirferl
2020-10-09 12:11:41 +02:00
19 changed files with 98 additions and 41 deletions

View File

@ -43,13 +43,13 @@ class AuthorityInputSchema(LemurInputSchema):
organization = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATION")
)
location = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_LOCATION")
)
location = fields.String()
country = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_COUNTRY")
)
state = fields.String(missing=lambda: current_app.config.get("LEMUR_DEFAULT_STATE"))
# Creating a String field instead of Email to allow empty value
email = fields.String()
plugin = fields.Nested(PluginInputSchema)

View File

@ -235,6 +235,7 @@ class Certificate(db.Model):
self.replaces = kwargs.get("replaces", [])
self.rotation = kwargs.get("rotation")
self.rotation_policy = kwargs.get("rotation_policy")
self.key_type = kwargs.get("key_type")
self.signing_algorithm = defaults.signing_algorithm(cert)
self.bits = defaults.bitstrength(cert)
self.external_id = kwargs.get("external_id")

View File

@ -107,9 +107,7 @@ class CertificateInputSchema(CertificateCreationSchema):
organization = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_ORGANIZATION")
)
location = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_LOCATION")
)
location = fields.String()
country = fields.String(
missing=lambda: current_app.config.get("LEMUR_DEFAULT_COUNTRY")
)
@ -155,6 +153,14 @@ class CertificateInputSchema(CertificateCreationSchema):
key_type = cert_utils.get_key_type_from_csr(data["csr"])
if key_type:
data["key_type"] = key_type
# This code will be exercised for certificate import (without CSR)
if data.get("key_type") is None:
if data.get("body"):
data["key_type"] = utils.get_key_type_from_certificate(data["body"])
else:
data["key_type"] = "RSA2048" # default value
return missing.convert_validity_years(data)
@ -277,6 +283,7 @@ class CertificateOutputSchema(LemurOutputSchema):
serial = fields.String()
serial_hex = Hex(attribute="serial")
signing_algorithm = fields.String()
key_type = fields.String(allow_none=True)
status = fields.String()
user = fields.Nested(UserNestedOutputSchema)
@ -317,6 +324,7 @@ class CertificateUploadInputSchema(CertificateCreationSchema):
body = fields.String(required=True)
chain = fields.String(missing=None, allow_none=True)
csr = fields.String(required=False, allow_none=True, validate=validators.csr)
key_type = fields.String()
destinations = fields.Nested(AssociatedDestinationSchema, missing=[], many=True)
notifications = fields.Nested(AssociatedNotificationSchema, missing=[], many=True)
@ -364,6 +372,16 @@ class CertificateUploadInputSchema(CertificateCreationSchema):
# Throws ValidationError
validators.verify_cert_chain([cert] + chain)
@pre_load
def load_data(self, data):
if data.get("body"):
try:
data["key_type"] = utils.get_key_type_from_certificate(data["body"])
except ValueError:
raise ValidationError(
"Public certificate presented is not valid.", field_names=["body"]
)
class CertificateExportInputSchema(LemurInputSchema):
plugin = fields.Nested(PluginInputSchema)

View File

@ -8,7 +8,7 @@ class DnsProvidersNestedOutputSchema(LemurOutputSchema):
__envelope__ = False
id = fields.Integer()
name = fields.String()
providerType = fields.String()
provider_type = fields.String()
description = fields.String()
credentials = fields.String()
api_endpoint = fields.String()

View File

@ -90,7 +90,7 @@ def update_key_type():
# Loop through all certificates that are valid today or expired in the last 30 days.
for cert_id, body in conn.execute(
text(
"select id, body from certificates where bits < 1024 and not_after > CURRENT_DATE - 31 and key_type is null")
"select id, body from certificates where not_after > CURRENT_DATE - 31 and key_type is null")
):
try:
cert_key_type = utils.get_key_type_from_certificate(body)

View File

@ -124,4 +124,8 @@ angular.module('lemur')
opened: false
};
$scope.populateSubjectEmail = function () {
$scope.authority.email = $scope.authority.owner;
};
});

View File

@ -26,8 +26,7 @@
Location
</label>
<div class="col-sm-10">
<input name="location" ng-model="authority.location" placeholder="Location" class="form-control" required/>
<p ng-show="dnForm.location.$invalid && !dnForm.location.$pristine" class="help-block">You must enter a location</p>
<input name="location" ng-model="authority.location" placeholder="Location" class="form-control"/>
</div>
</div>
<div class="form-group"
@ -49,6 +48,15 @@
<input name="organizationalUnit" ng-model="authority.organizationalUnit" placeholder="Organizational Unit" class="form-control"/>
</div>
</div>
<div class="form-group"
ng-class="{'has-error': dnForm.email.$invalid, 'has-success': !dnForm.$invalid&&dnForm.email.$dirty}">
<label class="control-label col-sm-2">
Email
</label>
<div class="col-sm-10">
<input type="email" name="email" ng-model="authority.email" placeholder="Email Address" class="form-control"/>
</div>
</div>
</div>
</form>

View File

@ -21,7 +21,7 @@
<div class="col-sm-10">
<input type="email" name="owner" ng-model="authority.owner" placeholder="TeamDL@example.com"
uib-tooltip="This is the authorities team distribution list or the main point of contact for this authority"
class="form-control" required/>
class="form-control" ng-change="populateSubjectEmail()" required/>
<p ng-show="trackingForm.owner.$invalid && !trackingForm.owner.$pristine" class="help-block">You must
enter an Certificate Authority owner</p>
</div>

View File

@ -251,10 +251,13 @@ angular.module('lemur')
$scope.certificate.csr = null; // should not clone CSR in case other settings are changed in clone
$scope.certificate.validityStart = null;
$scope.certificate.validityEnd = null;
$scope.certificate.keyType = 'RSA2048'; // default algo to show during clone
$scope.certificate.description = 'Cloning from cert ID ' + editId;
$scope.certificate.replacedBy = []; // should not clone 'replaced by' info
$scope.certificate.removeReplaces(); // should not clone 'replacement cert' info
if(!$scope.certificate.keyType) {
$scope.certificate.keyType = 'RSA2048'; // default algo to select during clone if backend did not return algo
}
CertificateService.getDefaults($scope.certificate);
});

View File

@ -38,9 +38,7 @@
Location
</label>
<div class="col-sm-10">
<input name="location" ng-model="certificate.location" placeholder="Location" class="form-control" required/>
<p ng-show="dnForm.location.$invalid && !dnForm.location.$pristine" class="help-block">You must enter a
location</p>
<input name="location" ng-model="certificate.location" placeholder="Location" class="form-control"/>
</div>
</div>
<div class="form-group"

View File

@ -111,6 +111,8 @@
<div class="list-group-item">
<dt>Key Length</dt>
<dd>{{ certificate.bits }}</dd>
<dt>Key Type</dt>
<dd>{{ certificate.keyType }}</dd>
<dt>Signing Algorithm</dt>
<dd>{{ certificate.signingAlgorithm }}</dd>
</div>

View File

@ -1,5 +1,6 @@
# This is just Python which means you can inherit and tweak settings
import base64
import os
import random
import string
@ -9,8 +10,10 @@ _basedir = os.path.abspath(os.path.dirname(__file__))
# generate random secrets for unittest
def get_random_secret(length):
input_ascii = string.ascii_letters + string.digits
return ''.join(random.choice(input_ascii) for i in range(length))
secret_key = ''.join(random.choice(string.ascii_uppercase) for x in range(round(length / 4)))
secret_key = secret_key + ''.join(random.choice("~!@#$%^&*()_+") for x in range(round(length / 4)))
secret_key = secret_key + ''.join(random.choice(string.ascii_lowercase) for x in range(round(length / 4)))
return secret_key + ''.join(random.choice(string.digits) for x in range(round(length / 4)))
THREADS_PER_PAGE = 8
@ -23,12 +26,14 @@ debug = False
TESTING = True
# this is the secret key used by flask session management
SECRET_KEY = "I/dVhOZNSMZMqrFJa5tWli6VQccOGudKerq3eWPMSzQNmHHVhMAQfQ=="
# this is the secret key used by flask session management (utf8 encoded)
SECRET_KEY = get_random_secret(length=32).encode('utf8')
# You should consider storing these separately from your config
# You should consider storing these separately from your config (should be URL-safe)
LEMUR_TOKEN_SECRET = "test"
LEMUR_ENCRYPTION_KEYS = "o61sBLNBSGtAckngtNrfVNd8xy8Hp9LBGDstTbMbqCY="
LEMUR_ENCRYPTION_KEYS = base64.urlsafe_b64encode(get_random_secret(length=32).encode('utf8'))
# List of domain regular expressions that non-admin users can issue
LEMUR_WHITELISTED_DOMAINS = [
@ -61,7 +66,8 @@ LEMUR_ALLOW_WEEKEND_EXPIRATION = False
# Database
# modify this if you are not using a local database
# modify this if you are not using a local database. Do not use any development or production DBs,
# as Unit Tests drop the whole schema, recreate and again drop everything at the end
SQLALCHEMY_DATABASE_URI = os.getenv(
"SQLALCHEMY_DATABASE_URI", "postgresql://lemur:lemur@localhost:5432/lemur"
)

View File

@ -154,7 +154,8 @@ def test_get_certificate_primitives(certificate):
with freeze_time(datetime.date(year=2016, month=10, day=30)):
primitives = get_certificate_primitives(certificate)
assert len(primitives) == 26
assert len(primitives) == 25
assert (primitives["key_type"] == "RSA2048")
def test_certificate_output_schema(session, certificate, issuer_plugin):
@ -253,17 +254,18 @@ def test_certificate_input_schema(client, authority):
"validityStart": arrow.get(2018, 11, 9).isoformat(),
"validityEnd": arrow.get(2019, 11, 9).isoformat(),
"dnsProvider": None,
"location": "A Place"
}
data, errors = CertificateInputSchema().load(input_data)
assert not errors
assert data["authority"].id == authority.id
assert data["location"] == "A Place"
# make sure the defaults got set
assert data["common_name"] == "test.example.com"
assert data["country"] == "US"
assert data["location"] == "Los Gatos"
assert len(data.keys()) == 19
@ -759,6 +761,7 @@ def test_reissue_certificate(
certificate.authority = crypto_authority
new_cert = reissue_certificate(certificate)
assert new_cert
assert (new_cert.key_type == "RSA2048")
def test_create_csr():

View File

@ -55,6 +55,7 @@ def test_create_pending(pending_certificate, user, session):
assert real_cert.notify == pending_certificate.notify
assert real_cert.private_key == pending_certificate.private_key
assert real_cert.external_id == "54321"
assert real_cert.key_type == "RSA2048"
@pytest.mark.parametrize(