diff --git a/dnstapir/key_cache.py b/dnstapir/key_cache.py index 5cb8611..2ef68b7 100644 --- a/dnstapir/key_cache.py +++ b/dnstapir/key_cache.py @@ -5,8 +5,32 @@ import redis from expiringdict import ExpiringDict from opentelemetry import trace +from pydantic import BaseModel, Field -tracer = trace.get_tracer("tapir.tracer") +tracer = trace.get_tracer("dnstapir.tracer") + + +class RedisSettings(BaseModel): + host: str = Field(description="Redis hostname") + port: int = Field(description="Redis port", default=6379) + + +class KeyCacheSettings(BaseModel): + size: int = Field(description="Cache size", default=1000) + ttl: int = Field(description="Cache TTL", default=300) + redis: RedisSettings | None = None + + +def key_cache_from_settings(settings: KeyCacheSettings): + memory_key_cache = MemoryKeyCache(size=settings.size, ttl=settings.ttl) + if settings.redis: + redis_client = redis.StrictRedis(host=settings.redis.host, port=settings.redis.port) + redis_key_cache = RedisKeyCache(redis_client=redis_client, ttl=settings.ttl) + return CombinedKeyCache([memory_key_cache, redis_key_cache]) if settings.size else redis_key_cache + elif settings.size: + return memory_key_cache + else: + return DummyKeyCache() class KeyCache: diff --git a/dnstapir/key_resolver.py b/dnstapir/key_resolver.py index c00904e..577b9eb 100644 --- a/dnstapir/key_resolver.py +++ b/dnstapir/key_resolver.py @@ -3,15 +3,19 @@ from urllib.parse import urljoin import httpx +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey +from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PublicKey +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from cryptography.hazmat.primitives.serialization import load_pem_public_key -from http_message_signatures import HTTPSignatureKeyResolver from opentelemetry import metrics, trace from .key_cache import KeyCache -tracer = trace.get_tracer("tapir.tracer") -meter = metrics.get_meter("tapir.meter") +type PublicKey = Ed25519PublicKey | Ed448PublicKey | EllipticCurvePublicKey | RSAPublicKey +tracer = trace.get_tracer("dnstapir.tracer") +meter = metrics.get_meter("dnstapir.meter") public_key_get_counter = meter.create_counter( "aggregates.public_key_get_counter", @@ -19,7 +23,20 @@ ) -class CacheKeyResolver(HTTPSignatureKeyResolver): +def key_resolver_from_client_database(client_database: str, key_cache: KeyCache | None = None): + if client_database.startswith("http://") or client_database.startswith("https://"): + return UrlKeyResolver(client_database_base_url=client_database, key_cache=key_cache) + else: + return FileKeyResolver(client_database_directory=client_database, key_cache=key_cache) + + +class KeyResolver: + @abstractmethod + def resolve_public_key(self, key_id: str) -> PublicKey: + pass + + +class CacheKeyResolver(KeyResolver): def __init__(self, key_cache: KeyCache | None): self.key_cache = key_cache diff --git a/poetry.lock b/poetry.lock index 83e5740..8042a9d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -516,41 +516,6 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] -[[package]] -name = "http-message-signatures" -version = "0.5.0" -description = "An implementation of the IETF HTTP Message Signatures draft standard" -optional = false -python-versions = "*" -files = [ - {file = "http-message-signatures-0.5.0.tar.gz", hash = "sha256:5a59de19b90dce0eaf62021ee776d6562e5a166c96e4107db36f9c01f25552a3"}, - {file = "http_message_signatures-0.5.0-py3-none-any.whl", hash = "sha256:719933cba48943b5e148fe7bbbf520927573f72c6ca00855cb2c79fdecee2cb2"}, -] - -[package.dependencies] -cryptography = ">=36.0.2" -http-sfv = ">=0.9.3" - -[package.extras] -tests = ["build", "coverage", "flake8", "mypy", "requests", "ruff", "wheel"] - -[[package]] -name = "http-sfv" -version = "0.9.9" -description = "Parse and serialise HTTP Structured Field Values" -optional = false -python-versions = ">=3.8" -files = [ - {file = "http_sfv-0.9.9-py3-none-any.whl", hash = "sha256:5feed51c90e9a1dc797701662d044d936923cf0027255c75452b8240e33d6c82"}, - {file = "http_sfv-0.9.9.tar.gz", hash = "sha256:e132dc9bef990832bc01824f5fa9d4efc7d0f4271e4b227db35c8ef38540c739"}, -] - -[package.dependencies] -typing-extensions = "*" - -[package.extras] -dev = ["black", "mypy"] - [[package]] name = "httpcore" version = "1.0.6" @@ -1504,4 +1469,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "0ad3544908af6eedf713e2987a6180255a4c665f0d8efebcd06d7e6573ce16bd" +content-hash = "2dabe0df1cf20cd530e451205a01335ee89506d77f734a5b64d10d115a9caf16" diff --git a/pyproject.toml b/pyproject.toml index 7c47f3d..1695bd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,19 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" +botocore = "^1.35.44" +expiringdict = "^1.2.2" +fastapi = "^0.115.2" +httpx = "^0.27.2" +jsonformatter = "^0.3.2" +opentelemetry-exporter-otlp = "^1.27.0" +opentelemetry-instrumentation-botocore = "^0.48b0" +opentelemetry-instrumentation-fastapi = "^0.48b0" +opentelemetry-instrumentation-pymongo = "^0.48b0" +pydantic = "^2.9.2" +pymongo = "^4.10.1" +redis = "^5.1.1" +cryptography = "^43.0.3" [tool.poetry.group.dev.dependencies] pytest = "^8.2.0" @@ -14,18 +27,6 @@ ruff = ">=0.7.0" pytest-ruff = "^0.4.1" fakeredis = "^2.25.1" pytest-httpx = "^0.32.0" -http-message-signatures = "^0.5.0" -jsonformatter = "^0.3.2" -opentelemetry-instrumentation-fastapi = "^0.48b0" -opentelemetry-exporter-otlp = "^1.27.0" -opentelemetry-instrumentation-botocore = "^0.48b0" -opentelemetry-instrumentation-pymongo = "^0.48b0" -httpx = "^0.27.2" -redis = "^5.1.1" -expiringdict = "^1.2.2" -fastapi = "^0.115.2" -botocore = "^1.35.44" -pymongo = "^4.10.1" [build-system] requires = ["poetry-core"]