Skip to content

Commit

Permalink
remove deprecation functions for cryptography utc
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasertl committed Jun 1, 2024
1 parent f786ff0 commit 8d60e96
Show file tree
Hide file tree
Showing 9 changed files with 28 additions and 76 deletions.
40 changes: 1 addition & 39 deletions ca/django_ca/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@
import functools
import typing
import warnings
from datetime import datetime, timezone as tz
from inspect import signature
from typing import Any, Optional, Union

from cryptography import x509
from typing import Any, Union

# IMPORTANT: Do **not** import any module from django_ca here, or you risk circular imports.

Expand Down Expand Up @@ -108,38 +105,3 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
return typing.cast(F, wrapper)

return decorator_deprecate


# pylint: disable=missing-function-docstring


def not_valid_before(certificate: x509.Certificate) -> datetime: # noqa: D103
if hasattr(certificate, "not_valid_before_utc"): # pragma: only cryptography>=42.0
return certificate.not_valid_before_utc
return certificate.not_valid_before.replace(tzinfo=tz.utc) # pragma: only cryptography<42.0


def not_valid_after(certificate: x509.Certificate) -> datetime: # noqa: D103
if hasattr(certificate, "not_valid_after_utc"): # pragma: only cryptography>=42.0
return certificate.not_valid_after_utc
return certificate.not_valid_after.replace(tzinfo=tz.utc) # pragma: only cryptography<42.0


def crl_next_update(crl: x509.CertificateRevocationList) -> Optional[datetime]: # noqa: D103
if hasattr(crl, "next_update_utc"): # pragma: only cryptography>=42.0
return crl.next_update_utc
if crl.next_update is not None: # pragma: cryptography<42.0 branch
return crl.next_update.replace(tzinfo=tz.utc)
return None # pragma: no cover


def crl_last_update(crl: x509.CertificateRevocationList) -> datetime: # noqa: D103
if hasattr(crl, "last_update_utc"): # pragma: only cryptography>=42.0
return crl.last_update_utc
return crl.last_update.replace(tzinfo=tz.utc) # pragma: only cryptography<42.0


def revoked_certificate_revocation_date(certificate: x509.RevokedCertificate) -> datetime: # noqa: D103
if hasattr(certificate, "revocation_date_utc"): # pragma: only cryptography>=42.0
return certificate.revocation_date_utc
return certificate.revocation_date.replace(tzinfo=tz.utc) # pragma: only cryptography<42.0
7 changes: 3 additions & 4 deletions ca/django_ca/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
from django_ca.acme.constants import BASE64_URL_ALPHABET, IdentifierType, Status
from django_ca.conf import CertificateRevocationListProfile, model_settings
from django_ca.constants import REVOCATION_REASONS, ReasonFlags
from django_ca.deprecation import not_valid_after, not_valid_before
from django_ca.extensions import get_extension_name
from django_ca.key_backends import KeyBackend, key_backends
from django_ca.managers import (
Expand Down Expand Up @@ -295,12 +294,12 @@ def issuer(self) -> x509.Name:
@property
def not_before(self) -> datetime:
"""A timezone-aware datetime representing the beginning of the validity period."""
return not_valid_before(self.pub.loaded)
return self.pub.loaded.not_valid_before_utc

@property
def not_after(self) -> datetime:
"""A timezone-aware datetime representing the end of the validity period."""
return not_valid_after(self.pub.loaded)
return self.pub.loaded.not_valid_after_utc

@property
def subject(self) -> x509.Name:
Expand Down Expand Up @@ -881,7 +880,7 @@ def generate_ocsp_key( # pylint: disable=too-many-locals
except Exception: # pragma: no cover # pylint: disable=broad-exception-caught
log.exception("Unknown error when reading existing OCSP responder certificate.")
else:
responder_certificate_expires = not_valid_after(responder_certificate)
responder_certificate_expires = responder_certificate.not_valid_after_utc
if responder_certificate_expires > now + model_settings.CA_OCSP_RESPONDER_CERTIFICATE_RENEWAL:
log.info("%s: OCSP responder certificate is not yet scheduled for renewal.")
return None
Expand Down
11 changes: 3 additions & 8 deletions ca/django_ca/tests/base/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@

from django_ca.conf import model_settings
from django_ca.constants import ReasonFlags
from django_ca.deprecation import (
RemovedInDjangoCA200Warning,
RemovedInDjangoCA220Warning,
crl_last_update,
crl_next_update,
)
from django_ca.deprecation import RemovedInDjangoCA200Warning, RemovedInDjangoCA220Warning
from django_ca.key_backends.storages import UsePrivateKeyOptions
from django_ca.models import Certificate, CertificateAuthority, X509CertMixin
from django_ca.signals import post_create_ca, post_issue_cert, pre_create_ca, pre_sign_cert
Expand Down Expand Up @@ -240,8 +235,8 @@ def assert_crl( # noqa: PLR0913
assert isinstance(parsed_crl.signature_hash_algorithm, type(algorithm))
assert parsed_crl.is_signature_valid(public_key) is True
assert parsed_crl.issuer == signer.pub.loaded.subject
assert crl_last_update(parsed_crl) == last_update
assert crl_next_update(parsed_crl) == expires_timestamp.replace(microsecond=0)
assert parsed_crl.last_update_utc == last_update
assert parsed_crl.next_update_utc == expires_timestamp.replace(microsecond=0)
assert list(parsed_crl.extensions) == extensions

entries = {e.serial_number: e for e in parsed_crl}
Expand Down
9 changes: 4 additions & 5 deletions ca/django_ca/tests/base/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import django

from django_ca.constants import EXTENSION_KEYS
from django_ca.deprecation import not_valid_after, not_valid_before
from django_ca.tests.base.typehints import CsrDict, KeyDict, PubDict
from django_ca.utils import add_colons

Expand Down Expand Up @@ -385,8 +384,8 @@ def _load_pub(data: dict[str, Any]) -> PubDict:
# Data derived from public key
_cert_data["issuer"] = _cert.issuer
_cert_data["serial_colons"] = add_colons(_cert_data["serial"])
_valid_from = not_valid_before(_cert)
_valid_until = not_valid_after(_cert)
_valid_from = _cert.not_valid_before_utc
_valid_until = _cert.not_valid_after_utc
_cert_data["valid_from"] = _valid_from
_cert_data["valid_until"] = _valid_until
_cert_data["valid_from_str"] = _cert_data["valid_from"].isoformat(" ")
Expand All @@ -411,8 +410,8 @@ def _load_pub(data: dict[str, Any]) -> PubDict:
TIMESTAMPS["profile_certs_valid"] = TIMESTAMPS["base"] + timedelta(days=12)

# When creating fixtures, latest valid_from of any generated cert is 20 days, we need to be after that
_root_cert = CERT_DATA["root-cert"]["pub"]["parsed"]
_time_base = not_valid_before(_root_cert)
_root_cert: x509.Certificate = CERT_DATA["root-cert"]["pub"]["parsed"]
_time_base = _root_cert.not_valid_before_utc

TIMESTAMPS["everything_valid"] = TIMESTAMPS["base"] + timedelta(days=23)
TIMESTAMPS["everything_valid_naive"] = TIMESTAMPS["everything_valid"].astimezone(tz.utc).replace(tzinfo=None)
Expand Down
2 changes: 0 additions & 2 deletions ca/django_ca/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import pytest
from _pytest.config import Config as PytestConfig
from _pytest.config.argparsing import Parser
from _pytest.python import Metafunc
from pytest_cov.plugin import CovPlugin

from ca import settings_utils # noqa: F401 # to get rid of pytest warnings for untested modules
Expand All @@ -39,7 +38,6 @@
generate_cert_fixture,
generate_pub_fixture,
generate_usable_ca_fixture,
interesting_certificate_names,
setup_pragmas,
usable_ca_names,
usable_cert_names,
Expand Down
7 changes: 3 additions & 4 deletions ca/django_ca/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@

from django_ca.conf import model_settings
from django_ca.constants import ReasonFlags
from django_ca.deprecation import not_valid_after, not_valid_before
from django_ca.key_backends.storages import UsePrivateKeyOptions
from django_ca.modelfields import LazyCertificate, LazyCertificateSigningRequest
from django_ca.models import (
Expand Down Expand Up @@ -874,7 +873,7 @@ def assertBasicCert(self, cert: x509.Certificate) -> None: # pylint: disable=in
"""Basic assertions about the certificate."""
now = datetime.now(tz=tz.utc)
self.assertEqual(cert.issuer, self.ca.subject)
self.assertEqual(not_valid_before(cert), now)
self.assertEqual(cert.not_valid_before_utc, now)
self.assertEqual(cert.version, x509.Version.v3)
self.assertIsInstance(cert.public_key(), rsa.RSAPublicKey)

Expand All @@ -898,7 +897,7 @@ def test_simple(self) -> None:
cert = self.ca.sign(key_backend_options, csr, subject=subject)

self.assertBasicCert(cert)
self.assertEqual(not_valid_after(cert), now + model_settings.CA_DEFAULT_EXPIRES)
self.assertEqual(cert.not_valid_after_utc, now + model_settings.CA_DEFAULT_EXPIRES)
self.assertEqual(cert.subject, subject)
self.assertIsInstance(cert.signature_hash_algorithm, type(self.ca.algorithm))
self.assertExtensionDict(
Expand All @@ -925,7 +924,7 @@ def test_non_default_values(self) -> None:
)

self.assertBasicCert(cert)
self.assertEqual(not_valid_after(cert), expires)
self.assertEqual(cert.not_valid_after_utc, expires)
self.assertIsInstance(cert.signature_hash_algorithm, hashes.SHA256)


Expand Down
24 changes: 12 additions & 12 deletions ca/django_ca/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import warnings
from datetime import datetime, timedelta, timezone as tz
from http import HTTPStatus
from typing import Any, Optional, Union
from typing import Any, Optional, Union, cast

from pydantic import BaseModel

Expand Down Expand Up @@ -109,13 +109,12 @@ def get_key_backend_options(self, ca: CertificateAuthority) -> BaseModel:
)
return ca.key_backend.get_use_private_key_options(ca, {"password": self.password})

def fetch_crl(
self, ca: CertificateAuthority, encoding: CertificateRevocationListEncodings
) -> x509.CertificateRevocationList:
def fetch_crl(self, ca: CertificateAuthority, encoding: CertificateRevocationListEncodings) -> bytes:
"""Actually fetch the CRL (nested function so that we can easily catch any exception)."""
cache_key = get_crl_cache_key(ca.serial, encoding=encoding, scope=self.scope)

crl = cache.get(cache_key)
if crl is None:
encoded_crl: Optional[bytes] = cache.get(cache_key)
if encoded_crl is None:
# Catch this case early so that we can give a better error message
if self.include_issuing_distribution_point is True and ca.parent is None and self.scope is None:
raise ValueError(
Expand All @@ -129,14 +128,15 @@ def fetch_crl(
scope=self.scope,
include_issuing_distribution_point=self.include_issuing_distribution_point,
)
crl = crl.public_bytes(encoding)
cache.set(cache_key, crl, self.expires)
return crl
encoded_crl = crl.public_bytes(encoding)
cache.set(cache_key, encoded_crl, self.expires)
return encoded_crl

def get(self, request: HttpRequest, serial: str) -> HttpResponse:
def get(self, request: HttpRequest, serial: str) -> HttpResponse: # pylint: disable=unused-argument
# pylint: disable=missing-function-docstring; standard Django view function
if get_encoding := request.GET.get("encoding"):
encoding = parse_encoding(get_encoding)
# TYPEHINT NOTE: type is verified in next line
encoding = cast(CertificateRevocationListEncodings, parse_encoding(get_encoding))
if encoding not in constants.CERTIFICATE_REVOCATION_LIST_ENCODING_NAMES:
return HttpResponseBadRequest(
f"{get_encoding}: Invalid encoding requested.", content_type="text/plain"
Expand All @@ -147,7 +147,7 @@ def get(self, request: HttpRequest, serial: str) -> HttpResponse:
ca = self.get_object()
try:
crl = self.fetch_crl(ca, encoding)
except Exception:
except Exception: # pylint: disable=broad-exception-caught
log.exception("Error generating a CRL")
return HttpResponseServerError("Error while retrieving the CRL.", content_type="text/plain")

Expand Down
2 changes: 1 addition & 1 deletion docs/source/changelog/TBR_1.29.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Python API

* :py:func:`django_ca.managers.CertificateManager.create_cert`:

* **BACKWARDS INCOMPATIBLE:** The `expires` parameter is now mandatory, and should be a timedelta or
* **BACKWARDS INCOMPATIBLE:** The `expires` parameter is now mandatory, and should be a ``timedelta`` or
timezone-aware datetime. Support for passing an ``int`` will be removed in 2.0.0.
* The `extensions` parameter no longer accepts extensions that are not valid for end entity certificates.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/deprecation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Python API

* :py:func:`django_ca.managers.CertificateManager.create_cert`:

* The `expires` parameter is now mandatory, and should be a timezone-aware datetime or a timedelta.
* The `expires` parameter is now mandatory, and should be a timezone-aware datetime or a ``timedelta``.
* The `extensions` parameter no longer accepts extensions that are not valid for end entity certificates.

* Removed ``django_ca.utils.parse_hash_algorithm()``, deprecated since 1.25.0. Use
Expand Down

0 comments on commit 8d60e96

Please sign in to comment.