Adds option to restrict certificate expiration dates to weekdays. (#453)
* Adding ability to restrict certificate creation to weekdays. * Ensuring that we test for weekends.
This commit is contained in:
parent
1b861baf0a
commit
dcb18a57c4
|
@ -51,7 +51,7 @@ Basic Configuration
|
|||
CORS = False
|
||||
|
||||
|
||||
.. data:: SQLACHEMY_DATABASE_URI
|
||||
.. data:: SQLALCHEMY_DATABASE_URI
|
||||
:noindex:
|
||||
|
||||
If you have ever used sqlalchemy before this is the standard connection string used. Lemur uses a postgres database and the connection string would look something like:
|
||||
|
@ -61,6 +61,11 @@ Basic Configuration
|
|||
SQLALCHEMY_DATABASE_URI = 'postgresql://<user>:<password>@<hostname>:5432/lemur'
|
||||
|
||||
|
||||
.. data:: LEMUR_ALLOW_WEEKEND_EXPIRATION
|
||||
:noindex:
|
||||
|
||||
Specifies whether to allow certificates created by Lemur to expire on weekends. Default is True.
|
||||
|
||||
.. data:: LEMUR_RESTRICTED_DOMAINS
|
||||
:noindex:
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class AuthorityInputSchema(LemurInputSchema):
|
|||
|
||||
@pre_load
|
||||
def ensure_dates(self, data):
|
||||
return missing.dates(data)
|
||||
return missing.convert_validity_years(data)
|
||||
|
||||
|
||||
class AuthorityUpdateSchema(LemurInputSchema):
|
||||
|
|
|
@ -72,7 +72,7 @@ class CertificateInputSchema(CertificateCreationSchema):
|
|||
|
||||
@pre_load
|
||||
def ensure_dates(self, data):
|
||||
return missing.dates(data)
|
||||
return missing.convert_validity_years(data)
|
||||
|
||||
|
||||
class CertificateEditInputSchema(CertificateSchema):
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
import arrow
|
||||
from flask import current_app
|
||||
|
||||
from lemur.common.utils import is_weekend
|
||||
|
||||
|
||||
def dates(data):
|
||||
# ensure that validity_start and validity_end are always set
|
||||
if not(data.get('validity_start') and data.get('validity_end')):
|
||||
def convert_validity_years(data):
|
||||
"""
|
||||
Convert validity years to validity_start and validity_end
|
||||
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
if data.get('validity_years'):
|
||||
num_years = data['validity_years']
|
||||
now = arrow.utcnow()
|
||||
then = now.replace(years=+int(num_years))
|
||||
data['validity_start'] = now.date().isoformat()
|
||||
|
||||
data['validity_start'] = now.isoformat()
|
||||
data['validity_end'] = then.isoformat()
|
||||
end = now.replace(years=+int(data['validity_years']))
|
||||
data['validity_end'] = end.date().isoformat()
|
||||
|
||||
if not current_app.config.get('LEMUR_ALLOW_WEEKEND_EXPIRATION', True):
|
||||
if is_weekend(end):
|
||||
end = end.replace(days=-2)
|
||||
data['validity_end'] = end.date().isoformat()
|
||||
|
||||
return data
|
||||
|
|
|
@ -41,3 +41,14 @@ def parse_certificate(body):
|
|||
return x509.load_pem_x509_certificate(body, default_backend())
|
||||
return x509.load_pem_x509_certificate(bytes(body, 'utf8'), default_backend())
|
||||
return x509.load_pem_x509_certificate(body.encode('utf-8'), default_backend())
|
||||
|
||||
|
||||
def is_weekend(date):
|
||||
"""
|
||||
Determines if a given date is on a weekend.
|
||||
|
||||
:param date:
|
||||
:return:
|
||||
"""
|
||||
if date.weekday() > 5:
|
||||
return True
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
|
||||
import arrow
|
||||
import re
|
||||
from flask import current_app
|
||||
from marshmallow.exceptions import ValidationError
|
||||
|
||||
from flask import current_app
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from marshmallow.exceptions import ValidationError
|
||||
|
||||
from lemur.common.utils import parse_certificate
|
||||
from lemur.domains import service as domain_service
|
||||
from lemur.auth.permissions import SensitiveDomainPermission
|
||||
from lemur.common.utils import parse_certificate, is_weekend
|
||||
from lemur.domains import service as domain_service
|
||||
|
||||
|
||||
def public_certificate(body):
|
||||
|
@ -102,6 +100,10 @@ def dates(data):
|
|||
raise ValidationError('If validity end is specified so must validity start.')
|
||||
|
||||
if data.get('validity_start') and data.get('validity_end'):
|
||||
if not current_app.config.get('LEMUR_ALLOW_WEEKEND_EXPIRATION', True):
|
||||
if is_weekend(data.get('validity_end')):
|
||||
raise ValidationError('Validity end must not land on a weekend.')
|
||||
|
||||
if not data['validity_start'] < data['validity_end']:
|
||||
raise ValidationError('Validity start must be before validity end.')
|
||||
|
||||
|
@ -112,13 +114,4 @@ def dates(data):
|
|||
if data.get('validity_end').replace(hour=0, minute=0, second=0, tzinfo=None) > data['authority'].authority_certificate.not_after.replace(hour=0, minute=0, second=0):
|
||||
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].authority_certificate.not_after))
|
||||
|
||||
if data.get('validity_years'):
|
||||
now = arrow.utcnow()
|
||||
end = now.replace(years=+data['validity_years'])
|
||||
|
||||
if data.get('authority'):
|
||||
if now.naive < data['authority'].authority_certificate.not_before:
|
||||
raise ValidationError('Validity start must not be before {0}'.format(data['authority'].authority_certificate.not_before))
|
||||
|
||||
if end.naive > data['authority'].authority_certificate.not_after:
|
||||
raise ValidationError('Validity end must not be after {0}'.format(data['authority'].authority_certificate.not_after))
|
||||
return data
|
||||
|
|
|
@ -46,6 +46,8 @@ LEMUR_DEFAULT_LOCATION = 'Los Gatos'
|
|||
LEMUR_DEFAULT_ORGANIZATION = 'Example, Inc.'
|
||||
LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = 'Example'
|
||||
|
||||
LEMUR_ALLOW_WEEKEND_EXPIRATION = False
|
||||
|
||||
# Database
|
||||
|
||||
# modify this if you are not using a local database
|
||||
|
|
|
@ -5,7 +5,7 @@ import json
|
|||
|
||||
from lemur.certificates.views import * # noqa
|
||||
|
||||
from .vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKEN, CSR_STR, \
|
||||
from lemur.tests.vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKEN, CSR_STR, \
|
||||
INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import arrow
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
|
||||
def test_convert_validity_years(session):
|
||||
from lemur.common.missing import convert_validity_years
|
||||
|
||||
with freeze_time("2016-01-01"):
|
||||
data = convert_validity_years(dict(validity_years=2))
|
||||
|
||||
assert data['validity_start'] == arrow.utcnow().date().isoformat()
|
||||
assert data['validity_end'] == arrow.utcnow().replace(years=+2).date().isoformat()
|
||||
|
||||
with freeze_time("2015-01-10"):
|
||||
data = convert_validity_years(dict(validity_years=1))
|
||||
assert data['validity_end'] == arrow.utcnow().replace(years=+1, days=-2).date().isoformat()
|
|
@ -1,29 +1,32 @@
|
|||
from marshmallow.exceptions import ValidationError
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from .vectors import PRIVATE_KEY_STR
|
||||
from marshmallow.exceptions import ValidationError
|
||||
|
||||
|
||||
def test_private_key():
|
||||
def test_private_key(session):
|
||||
from lemur.common.validators import private_key
|
||||
try:
|
||||
|
||||
private_key(PRIVATE_KEY_STR)
|
||||
assert True
|
||||
except ValidationError:
|
||||
assert False, "failed to validate private key as a bytes object"
|
||||
|
||||
|
||||
def test_private_key_str_object():
|
||||
from lemur.common.validators import private_key
|
||||
try:
|
||||
private_key(PRIVATE_KEY_STR.decode('utf-8'))
|
||||
assert True
|
||||
except ValidationError:
|
||||
assert False, "failed to validate private key as a str object"
|
||||
|
||||
|
||||
def test_private_key_invalid():
|
||||
from lemur.common.validators import private_key
|
||||
try:
|
||||
with pytest.raises(ValidationError):
|
||||
private_key('invalid_private_key')
|
||||
assert False, "invalid private key should have raised an exception"
|
||||
except ValidationError:
|
||||
assert True
|
||||
|
||||
|
||||
def test_dates(session):
|
||||
from lemur.common.validators import dates
|
||||
|
||||
dates(dict(validity_start=datetime(2016, 1, 1), validity_end=datetime(2016, 1, 5)))
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
dates(dict(validity_start=datetime(2016, 1, 1)))
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
dates(dict(validity_end=datetime(2016, 1, 1)))
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
dates(dict(validity_start=datetime(2016, 1, 5), validity_end=datetime(2016, 1, 1)))
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
dates(dict(validity_start=datetime(2016, 1, 1), validity_end=datetime(2016, 1, 10)))
|
||||
|
|
Loading…
Reference in New Issue