From 7d6d49564e732df9a9faaa696f8d8e6915717d6e Mon Sep 17 00:00:00 2001 From: PatStLouis Date: Fri, 4 Oct 2024 16:24:02 +0000 Subject: [PATCH] updated dependencies and reformatting Signed-off-by: PatStLouis --- server/app/__init__.py | 2 + server/app/models/did_document.py | 33 +++++++++------ server/app/plugins/askar.py | 14 ++----- server/app/routers/identifiers.py | 67 +++++++++++++++++-------------- server/config.py | 4 +- server/requirements | 0 server/requirements copy.txt | 36 +++++++++++++++++ server/requirements.txt | 25 +++--------- 8 files changed, 106 insertions(+), 75 deletions(-) create mode 100644 server/requirements create mode 100644 server/requirements copy.txt diff --git a/server/app/__init__.py b/server/app/__init__.py index 7674eac..81c3c00 100644 --- a/server/app/__init__.py +++ b/server/app/__init__.py @@ -7,10 +7,12 @@ api_router = APIRouter() + @api_router.get("/server/status", tags=["Server"], include_in_schema=False) async def server_status(): return JSONResponse(status_code=200, content={"status": "ok"}) + api_router.include_router(identifiers.router) app.include_router(api_router) diff --git a/server/app/models/did_document.py b/server/app/models/did_document.py index 63017b7..a4becaa 100644 --- a/server/app/models/did_document.py +++ b/server/app/models/did_document.py @@ -7,18 +7,18 @@ import validators -DID_WEB_REGEX = re.compile( - "did:web:((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)" -) +DID_WEB_REGEX = re.compile("did:web:((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)") DID_WEB_ID_REGEX = re.compile( "did:web:((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)#([a-z0-9._%-]+)" ) + class BaseModel(BaseModel): def model_dump(self, **kwargs) -> Dict[str, Any]: return super().model_dump(by_alias=True, exclude_none=True, **kwargs) + class VerificationMethod(BaseModel): id: str = Field() type: Union[str, List[str]] = Field() @@ -27,27 +27,31 @@ class VerificationMethod(BaseModel): @field_validator("id") @classmethod def verification_method_id_validator(cls, value): - assert value.startswith(f'did:web:{settings.DOMAIN}') + assert value.startswith(f"did:web:{settings.DOMAIN}") return value @field_validator("type") @classmethod def verification_method_type_validator(cls, value): - assert value in ['Multikey', 'JsonWebKey'], 'Expected type Multikey or JsonWebKey' + assert value in [ + "Multikey", + "JsonWebKey", + ], "Expected type Multikey or JsonWebKey" return value @field_validator("controller") @classmethod def verification_method_controller_validator(cls, value): assert DID_WEB_REGEX.match(value), "Expected controller to be a DID." - assert value.startswith(f'did:web:{settings.DOMAIN}') + assert value.startswith(f"did:web:{settings.DOMAIN}") return value class JsonWebKey(BaseModel): - kty: str = Field('OKP') - crv: str = Field('Ed25519') - x: str = Field(example='jihLNQ0eeR8OR-bgVxiUNOTP0tDKs5WKypYN0J5SJ9I') + kty: str = Field("OKP") + crv: str = Field("Ed25519") + x: str = Field(example="jihLNQ0eeR8OR-bgVxiUNOTP0tDKs5WKypYN0J5SJ9I") + class VerificationMethodJwk(VerificationMethod): publicKeyJwk: JsonWebKey = Field() @@ -58,6 +62,7 @@ def verification_method_public_key_validator(cls, value): # TODO decode b64 return value + class VerificationMethodMultikey(VerificationMethod): publicKeyMultibase: str = Field() @@ -67,7 +72,7 @@ def verification_method_public_key_validator(cls, value): try: multibase.decode(value) except: - assert False, f'Unable to decode public key multibase value {value}' + assert False, f"Unable to decode public key multibase value {value}" return value @@ -79,13 +84,13 @@ class Service(BaseModel): @field_validator("id") @classmethod def service_id_validator(cls, value): - assert value.startswith(f'did:web:{settings.DOMAIN}') + assert value.startswith(f"did:web:{settings.DOMAIN}") return value @field_validator("serviceEndpoint") @classmethod def service_endpoint_validator(cls, value): - assert validators.url(value) , f"Invalid service endpoint {value}." + assert validators.url(value), f"Invalid service endpoint {value}." return value @@ -98,7 +103,9 @@ class DidDocument(BaseModel): description: str = Field(None) controller: str = Field(None) alsoKnownAs: List[str] = Field(None) - verificationMethod: List[Union[VerificationMethodMultikey, VerificationMethodJwk]] = Field() + verificationMethod: List[ + Union[VerificationMethodMultikey, VerificationMethodJwk] + ] = Field() authentication: List[Union[str, VerificationMethod]] = Field() assertionMethod: List[Union[str, VerificationMethod]] = Field() keyAgreement: List[Union[str, VerificationMethod]] = Field(None) diff --git a/server/app/plugins/askar.py b/server/app/plugins/askar.py index 4a895f2..c876ce8 100644 --- a/server/app/plugins/askar.py +++ b/server/app/plugins/askar.py @@ -37,11 +37,7 @@ async def store(self, category, data_key, data): store = await self.open() try: async with store.session() as session: - await session.insert( - category, - data_key, - json.dumps(data) - ) + await session.insert(category, data_key, json.dumps(data)) except: raise HTTPException(status_code=404, detail="Couldn't store record.") @@ -49,11 +45,7 @@ async def update(self, category, data_key, data): store = await self.open() try: async with store.session() as session: - await session.replace( - category, - data_key, - json.dumps(data) - ) + await session.replace(category, data_key, json.dumps(data)) except: raise HTTPException(status_code=404, detail="Couldn't update record.") @@ -103,7 +95,7 @@ def assert_proof_options(self, proof, did): raise HTTPException(status_code=400, detail=str(msg)) def verify_proof(self, document, proof): - self.assert_proof_options(proof, document['id']) + self.assert_proof_options(proof, document["id"]) multikey = proof["verificationMethod"].split("#")[-1] diff --git a/server/app/routers/identifiers.py b/server/app/routers/identifiers.py index 5f5eced..028c7f1 100644 --- a/server/app/routers/identifiers.py +++ b/server/app/routers/identifiers.py @@ -20,10 +20,8 @@ async def request_did( status_code=200, content={ "didDocument": { - "@context": [ - "https://www.w3.org/ns/did/v1" - ], - "id": did + "@context": ["https://www.w3.org/ns/did/v1"], + "id": did, }, "proofOptions": AskarVerifier().create_proof_config(did), }, @@ -34,56 +32,65 @@ async def request_did( @router.post("/{namespace}/{identifier}") async def register_did( - namespace: str, identifier: str, request_body: RegisterDID, + namespace: str, + identifier: str, + request_body: RegisterDID, ): - did_document = request_body.model_dump()['didDocument'] + did_document = request_body.model_dump()["didDocument"] did = f"{settings.DID_WEB_BASE}:{namespace}:{identifier}" - + await identifier_available(did) - + # Ensure correct endpoint is called - if did_document['id'] != did: + if did_document["id"] != did: raise HTTPException(status_code=400, detail="Location mismatch.") - + # Assert proof set proof_set = did_document.pop("proof", None) if len(proof_set) != 2: raise HTTPException(status_code=400, detail="Expecting proof set.") - + # Find proof matching endorser - endorser_proof = next(( - proof for proof in proof_set - if proof['verificationMethod'] == - f"did:key:{settings.ENDORSER_MULTIKEY}#{settings.ENDORSER_MULTIKEY}" - ), None) - + endorser_proof = next( + ( + proof + for proof in proof_set + if proof["verificationMethod"] + == f"did:key:{settings.ENDORSER_MULTIKEY}#{settings.ENDORSER_MULTIKEY}" + ), + None, + ) + # Find proof matching client - client_proof = next(( - proof for proof in proof_set - if proof['verificationMethod'] != - f"did:key:{settings.ENDORSER_MULTIKEY}#{settings.ENDORSER_MULTIKEY}" - ), None) - + client_proof = next( + ( + proof + for proof in proof_set + if proof["verificationMethod"] + != f"did:key:{settings.ENDORSER_MULTIKEY}#{settings.ENDORSER_MULTIKEY}" + ), + None, + ) + if client_proof and endorser_proof: # Verify proofs AskarVerifier().verify_proof(did_document, client_proof) AskarVerifier().verify_proof(did_document, endorser_proof) - authorized_key = client_proof['verificationMethod'].split('#')[-1] - + authorized_key = client_proof["verificationMethod"].split("#")[-1] + # TODO implement registration queue # await AskarStorage().store("didRegistration", did, did_document) - + # Store document and authorized key await AskarStorage().store("didDocument", did, did_document) await AskarStorage().store("authorizedKey", did, authorized_key) return JSONResponse(status_code=201, content={"didDocument": did_document}) - + raise HTTPException(status_code=400, detail="Missing expected proof.") + @router.get("/{namespace}/{identifier}/did.json", include_in_schema=False) -async def get_did_document( - namespace: str, identifier: str -): +async def get_did_document(namespace: str, identifier: str): did = f"{settings.DID_WEB_BASE}:{namespace}:{identifier}" did_doc = await AskarStorage().fetch("didDocument", did) if did_doc: diff --git a/server/config.py b/server/config.py index 988b986..77e75e5 100644 --- a/server/config.py +++ b/server/config.py @@ -9,13 +9,13 @@ class Settings(BaseSettings): PROJECT_TITLE: str = "TDW Server" PROJECT_VERSION: str = "v0" - + SECRET_KEY: str = os.environ["SECRET_KEY"] DOMAIN: str = os.environ["DOMAIN"] DID_WEB_BASE: str = f"did:web:{DOMAIN}" ENDORSER_MULTIKEY: str = os.environ["ENDORSER_MULTIKEY"] - + ASKAR_DB: str = ( os.environ["POSTGRES_URI"] if "POSTGRES_URI" in os.environ diff --git a/server/requirements b/server/requirements new file mode 100644 index 0000000..e69de29 diff --git a/server/requirements copy.txt b/server/requirements copy.txt new file mode 100644 index 0000000..ea4770a --- /dev/null +++ b/server/requirements copy.txt @@ -0,0 +1,36 @@ +annotated-types==0.7.0 +anyio==4.4.0 +aries-askar==0.3.2 +base58==2.1.1 +bases==0.3.0 +black==24.8.0 +cached-property==1.5.2 +canonicaljson==2.0.0 +cffi==1.17.1 +click==8.1.7 +exceptiongroup==1.2.2 +fastapi==0.112.0 +h11==0.14.0 +idna==3.10 +inflection==0.5.1 +multiformats==0.3.1.post4 +multiformats-config==0.3.1 +mypy-extensions==1.0.0 +packaging==24.1 +pathspec==0.12.1 +platformdirs==4.3.2 +pycparser==2.22 +pydantic==2.9.2 +pydantic-settings==2.5.2 +pydantic_core==2.24.0 +pydid==0.5.1 +PyNaCl==1.5.0 +python-dotenv==1.0.1 +ruff==0.6.2 +sniffio==1.3.1 +starlette==0.39.2 +tomli==2.0.1 +typing-validation==1.2.11.post4 +typing_extensions==4.12.2 +uvicorn==0.30.6 +validators==0.34.0 diff --git a/server/requirements.txt b/server/requirements.txt index ea4770a..99dae6c 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -1,36 +1,23 @@ annotated-types==0.7.0 -anyio==4.4.0 +anyio==4.6.0 aries-askar==0.3.2 -base58==2.1.1 bases==0.3.0 -black==24.8.0 cached-property==1.5.2 canonicaljson==2.0.0 -cffi==1.17.1 click==8.1.7 -exceptiongroup==1.2.2 -fastapi==0.112.0 +fastapi==0.115.0 h11==0.14.0 idna==3.10 -inflection==0.5.1 multiformats==0.3.1.post4 multiformats-config==0.3.1 -mypy-extensions==1.0.0 -packaging==24.1 -pathspec==0.12.1 -platformdirs==4.3.2 -pycparser==2.22 pydantic==2.9.2 pydantic-settings==2.5.2 -pydantic_core==2.24.0 -pydid==0.5.1 -PyNaCl==1.5.0 +pydantic_core==2.23.4 python-dotenv==1.0.1 -ruff==0.6.2 +ruff==0.6.9 sniffio==1.3.1 -starlette==0.39.2 -tomli==2.0.1 +starlette==0.38.6 typing-validation==1.2.11.post4 typing_extensions==4.12.2 -uvicorn==0.30.6 +uvicorn==0.31.0 validators==0.34.0