Skip to content

Commit

Permalink
wip http clients database
Browse files Browse the repository at this point in the history
  • Loading branch information
jschlyter committed Oct 9, 2024
1 parent 2ef5d46 commit 6e3ae4b
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 17 deletions.
1 change: 1 addition & 0 deletions aggrec.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
clients_database = "clients"
#clients_database = "http://localhost:8081"

metadata_base_url= "http://127.0.0.1:8080"

Expand Down
57 changes: 42 additions & 15 deletions aggrec/helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import hashlib
import logging
from datetime import datetime, timezone
from urllib.parse import urljoin

import http_sf
import httpx
import pendulum
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from fastapi import HTTPException, Request, status
Expand All @@ -15,25 +17,13 @@
)
from http_message_signatures.algorithms import signature_algorithms as supported_signature_algorithms
from http_message_signatures.exceptions import InvalidSignature
from pydantic import AnyHttpUrl, DirectoryPath
from werkzeug.utils import safe_join

DEFAULT_SIGNATURE_ALGORITHM = algorithms.ECDSA_P256_SHA256
HASH_ALGORITHMS = {"sha-256": hashlib.sha256, "sha-512": hashlib.sha512}


class MyHTTPSignatureKeyResolver(HTTPSignatureKeyResolver):
def __init__(self, client_database: str):
self.client_database = client_database

def resolve_public_key(self, key_id: str):
filename = safe_join(self.client_database, f"{key_id}.pem")
try:
with open(filename, "rb") as fp:
return load_pem_public_key(fp.read())
except FileNotFoundError as exc:
raise KeyError(key_id) from exc


class ContentDigestException(ValueError):
pass

Expand All @@ -50,15 +40,52 @@ class ContentDigestMissing(ContentDigestException):
pass


class FileKeyResolver(HTTPSignatureKeyResolver):
def __init__(self, client_database_directory: str):
self.client_database_directory = client_database_directory

def resolve_public_key(self, key_id: str):
filename = safe_join(self.client_database_directory, f"{key_id}.pem")
try:
with open(filename, "rb") as fp:
return load_pem_public_key(fp.read())
except FileNotFoundError as exc:
raise KeyError(key_id) from exc


class UrlKeyResolver(HTTPSignatureKeyResolver):
def __init__(self, client_database_base_url: str):
self.client_database_base_url = client_database_base_url
self.httpx_client = httpx.Client()

def resolve_public_key(self, key_id: str):
public_key_url = urljoin(self.client_database_base_url, f"{key_id}.pem")
try:
response = self.httpx_client.get(public_key_url)
response.raise_for_status()
return load_pem_public_key(response.content)
except httpx.HTTPError as exc:
raise KeyError(key_id) from exc


class RequestVerifier:
def __init__(
self,
algorithm: HTTPSignatureAlgorithm | None = None,
key_resolver: HTTPSignatureKeyResolver | None = None,
client_database: str | None = None,
client_database: AnyHttpUrl | DirectoryPath | None = None,
):
self.algorithm = algorithm or DEFAULT_SIGNATURE_ALGORITHM
self.key_resolver = MyHTTPSignatureKeyResolver(client_database) if client_database else key_resolver
if key_resolver:
self.key_resolver = key_resolver
elif client_database and (
str(client_database).startswith("http://") or str(client_database).startswith("https://")
):
self.key_resolver = UrlKeyResolver(str(client_database))
elif client_database:
self.key_resolver = FileKeyResolver(str(client_database))
else:
raise ValueError("No key resolver nor client database specified")
self.logger = logging.getLogger(__name__).getChild(self.__class__.__name__)

async def verify_content_digest(self, result: VerifyResult, request: Request):
Expand Down
2 changes: 1 addition & 1 deletion aggrec/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class OtlpSettings(BaseModel):

class Settings(BaseSettings):
metadata_base_url: AnyHttpUrl = Field(default="http://127.0.0.1")
clients_database: DirectoryPath = Field(default="clients")
clients_database: DirectoryPath | AnyHttpUrl = Field(default="clients")
s3: S3 = Field(default=S3())
mqtt: MqttSettings = Field(default=MqttSettings())
mongodb: MongoDB = Field(default=MongoDB())
Expand Down
24 changes: 24 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ services:
- 127.0.0.1:27017:27017/tcp
volumes:
- mongo:/data/db
caddy:
image: caddy:2
ports:
- "8081:8081/tcp"
volumes:
- ./clients:/data/clients:ro,z
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
redis:
image: redis/redis-stack:latest
ports:
- 6379:6379/tcp
- 8001:8001/tcp
mosquitto:
image: eclipse-mosquitto:latest
restart: unless-stopped
Expand Down Expand Up @@ -59,6 +73,16 @@ configs:
content: |
listener 1883
allow_anonymous true
Caddyfile:
content: |
http://localhost:8081 {
encode gzip
root /data/clients
file_server
log {
output stdout
}
}
otel-collector-config.yaml:
content: |
receivers:
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ opentelemetry-exporter-otlp = "^1.27.0"
opentelemetry-instrumentation-botocore = "^0.48b0"
opentelemetry-instrumentation-pymongo = "^0.48b0"
http-sf = "^1.0.2"
httpx = "^0.27.2"

[tool.poetry.group.dev.dependencies]
pytest = "^8.2.0"
Expand Down

0 comments on commit 6e3ae4b

Please sign in to comment.